前言
平时喜欢在公众号中看一些文章,虽然微信中能直接搜索文章,但是如果想看某个公众号的系列文章,需要关注公众号,这样就会导致关注的公众号越来越多了。偶然发现一个网站可以直接搜索微信公众号(网站地址
)在pc端浏览或者使用手机浏览器阅读都不是很方便,所以就自己打算实现一个搜索公众号的应用,目前实现了公众号的搜索,公众号文章浏览,无需关注。后续还会增加分类,收藏,推荐等。下面先看一下运行效果图。
获取数据
由于没有专用的接口,所以数据直接爬取上述提到的网站,工具使用Jsoup,这里不再讲述Jsoup的使用,很容易上手的,下面直接贴出教程的地址
jsoup官方文档:
https://jsoup.org/cookbook/
中文文档:
http://www.open-open.com/jsoup/
下面详细说一下数据的爬取过程
我们需要获取公众号的头像,名字,功能介绍,认证等信息,我们就需要找到对应的Html标签,以及选择器的名字,因为我们搜索出来的是一些公众号,所以在html中肯定是以列表展示的,我们直接查看代码找到对应的列表
在hmtl中列表是以类选择器为“new-list2”的ul来展示的,所以每一个公众号的信息是由li标签来展示的,所以直接看一下li标签的结构
通过分析li标签中的结构我们可以发现每一个我们需要的信息对应的标签名字和对应的选择器的名字,我们通过jsoup这个库直接获取我们所需要的信息就可以了,每个公众号都具有相同的结构,所以我们在代码中循环遍历ul标签然后获取每一个li标签内的信息即可。下面是爬取数据的具体代码:
@Override
public void run() {
try {
isRun = true;
datas = new ArrayList<>();
Document doc = Jsoup.connect(urlStr).get();
Element ulElem = doc.select("ul.news-list2").first();
Elements liElems = ulElem.select("li");
for (int i = 0; i < liElems.size(); i++) {
Element element = liElems.get(i);//每一项的li标签
Elements divImg = element.select("div.img-box");
String itemUrl = divImg.select("a[href]").attr("href");//item跳转链接
String imgUrl = divImg.select("img").attr("src");//头像地址
Elements divText = element.select("div.txt-box");
String account = divText.select("label").text();//微信账号
String count = divText.select("span").text();//月发文数量
Elements dls = element.select("dl");
Elements aElm = divText.select("a");
String name = aElm.text();
String article = "";
String articleUrl = "";
String function = "";
String authentication = "";
ArrayList<String> strs = new ArrayList<>();
for (int i1 = 0; i1 < dls.size(); i1++) {
Element dl = dls.get(i1);
if (i1 == dls.size() - 1) {
Elements aEml = dl.select("dd").select("a");
article = aEml.text();
articleUrl = aEml.attr("href");
break;
}
strs.add(dl.select("dd").text());
}
if (strs.size() == 2) {
function = strs.get(0);
authentication = strs.get(1);
}
ItemBean itemBean = new ItemBean(itemUrl, saveFile.getAbsolutePath(), name, account, count, function,
authentication, article, articleUrl);//将数据封装到bean对象中
datas.add(itemBean);
} catch (Exception e) {
e.printStackTrace();
}
}
通过日志打印的信息我们看到,我们正确的获取了所需要的数据
拿到数据后我们就可以将数据以列表的形式展示到页面上了,由于爬取数据中公众号头像地址中有的没有图片格式后缀名,那么在Android中使用图片工具类展示图片是不成功的,所以还是使用jsoup在爬取数据的过程中先把头像图片下载下来保存在本地,然后将图片的保存地址添加到bean对象中,在再去设置头像的时候,直接从本地读取。
//下载头像图片
Connection.Response response = Jsoup.connect(imgUrl).timeout(10 * 1000).ignoreContentType(true).execute();
File saveFile = new File(App.context.getCacheDir().getPath() + "/" + Md5Utils.md5(imgUrl) + ".jpg");
if(!saveFile.exists())saveFile.createNewFile();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(saveFile));
bos.write(response.bodyAsBytes());
bos.flush();
bos.close();
展示数据
item的布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginRight="6dp"
android:layout_marginLeft="6dp"
card_view:cardBackgroundColor="@color/item_card_bg"
card_view:cardCornerRadius="1dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="功能介绍"
android:textColor="#8e8c8c"
android:textSize="16sp"
android:visibility="invisible"/>
<com.wx.ovalimageview.RoundImageView
android:id="@+id/img_item"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_centerInParent="true"
android:scaleType="fitCenter"
card_view:circle="true"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/tv_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=":"
android:textColor="#8e8c8c"
android:textSize="16sp"
android:visibility="invisible"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/tv_left"
android:textColor="@color/item_name_text"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_name"
android:layout_marginTop="4dp"
android:text="微信号:"
android:layout_toRightOf="@+id/tv_left"
android:textColor="@color/item_info_text"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_acount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/tv_item"
android:layout_toRightOf="@+id/tv_item"
android:textColor="@color/item_info_text"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/tv_item"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:textColor="@color/item_info_text"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/llay_fun"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="功能介绍:"
android:textColor="@color/item_info_text"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_func"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/item_fun_text"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/llay_authen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="微信认证:"
android:textColor="@color/item_info_text"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_authentication"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/item_fun_text"
android:textSize="16sp" />
</LinearLayout>
<RelativeLayout
android:id="@+id/relay_article"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="最近文章:"
android:textColor="@color/item_info_text"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_article"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/tv_time"
android:layout_toRightOf="@+id/tv"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/item_fun_text"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textColor="@color/item_info_text"/>
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
在搜索相关的公众号时,搜索结果有很多,我们一次搜索只获取一页,然后通过上拉加载来获取下一页,直到最后一页数据。
private String url = " http://weixin.sogou.com/weixin?page=%d&query=%s";//数据请求地址
请求的接口就这一个,我们只需要将搜索框中输入的关键字替换掉第二个参数,然后查询第几页替换掉第一个参数即可。
urlStr = String.format(url, page, keyword);//请求时正真的请求地址
最后我们在跳转到公众号详情的时候,是用WebView加载一个网页。这里推荐一个WebView的库,它对WebView做了封装处理,使用简单,功能强大。
最后
代码已经提交到github