一个用于爬取YorkBBS住房信息的NodeJS动态网页爬虫

1.目的

这个project用于爬取YorkBBS上完整的住房信息,并将其生成一个html,放入apache2的服务器下,使之可以在任何地方查看筛选好的信息。

2.网页解析以及数据接口获取

YorkBBS采用的是动态网页加载,如果采用http解析器直接采集DOM结构的话,只能得到一个参杂了JS代码的HTML结构。这里有两个思路:

  1. 使用诸如PhantomnJS之类的库,等待网页加载完成才进行DOM读取

  2. 使用Chrome DevTool,通过Network功能查找和服务器端的通讯从而判断出数据的API接口,并通过伪造Header获取相应的信息

    这里我们采用第二个思路,首先打开Chrome DevTool 的Network标签,刷新目标网页,观察和服务器目标中的通讯。我们很快发现了这个Header:

如何使用Android Studio自带的http解析器制作简单的静态网页爬虫APP

1.使用Andriod Studio的http解析器

​ Android Studio 在高版本后自带了Jsoup解析器,要使用Jsoup解析器,首先要在

scripts:build.gradle:dependencies```中加入```implementation 'org.jsoup:jsoup:1.9.2'``` ,其后,在class里可以使用```import org.jsoup.Jsoup;``` 导入。Android Studio 默认关闭了APP的网络访问,所以要在```manifests:AndroidManifest.xml```
1
2
3
4

中加入两行代码

```<usespermissionandroid:name="android.permission.INTERNET" />
android:name
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- more --> 
#### 2.获取以及解析静态网页

##### (1)获取网页

首先创建一个```private Document getDocument(Stirng url)``` ,以Math1310的课程网页为例,令```String url = "http://math.yorku.ca/~lishu3/math1310.html" ``` 此时,构建一个简单的 try catch模块。

```java
try{
return Jsoup.connect(url).timeout(5000).get();
}catch (IOException e){
e.printStackTrace();
}

需要注意的是,Jsoup.connect后需要加get()才能取得解析结果,而timeout则要加在两者之间。

这个function 返回的Document格式是Jsoup独有的格式。所以需要导入import org.jsoup.nodes.Document才不会报错。

(2)解析网页

首先分析网页结构

需要抓取的结构通过分析我们可以看出位于<ul></ul>内 , 此时令Document doc = getDocument(url) 添加Jsoup选择器Element ul = doc.select("ul").first() 这段代码的意思是从整个被解析出的html文件中选取第一个ul作为Element。需要注意的是,这个网页中含有两个<ul>element 而如果不使用.first()去选择第一个ul作为element则必须使用Elements作为数据容器。

此时我们已经选择第一个<ul>作为目标,使用 Elements elements= ul.select("li");选取ul内所有的li作为目标。 迭代循环并输出每一个element的内容。

3.多线程更新View

在Android中,原始的主线程不允许参与与网络有关的模块运行,因为非常容易造成卡顿,那么要运行刚刚的程序,必须开启一个新的线程。

1
2
3
4
5
new Thread(){
@Override
public void run(){
}
}.start();

而如果要更新View,则必须使用Handler才能进行

1
2
3
Handler  handler=new Handler();
以及
handler.post();

但是如果直接使用两者来setContentoftextView就会遇到一个问题:报错

1
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

翻译过来就是,只有创建view的那个线程(这里是主线程)才能跟新view,而我们创建的子线程new thread如果直接运行setContentOfTextView则会报错。

这里就要使用这种方法才能避开这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Thread(){
@Override
public void run(){
GetInfo info=new GetInfo();
final String output=info.getInfo("http://math.yorku.ca/~lishu3/math1310.html");
handler.post(new Runnable() {
@Override
public void run() {
setContentOftextView(R.id.outputInfo,output);
}
});
}

}.start();

通过在handler.post里再创建一个新的Runnable event在里面跑setContentofTextView就可以成功的避开这个问题了。

4.GGWP其他一些小的经验

(1)在设置textView的时候,如果加一行textView.setMovementMethod(ScrollingMovementMethod.getInstance());

则textview可以触屏滚动

(2)在制作这个简单的爬虫APP的时候,我并没有考虑脱离网络的情况,实际上,如果不加入判断手机是否有网络的代码,则这个APP会直接崩溃,推荐在之后的APP里加入判断手机是否有Wifi或者是数据的功能。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×