如果你想利用自己的技术做出一点有意思的产品来,那么爬虫、算法和 AI 等技术可能是一个不错的突破口。今天,我们就来介绍下使用 Java 爬取页面信息的几种思路。
说起爬虫,自从 Python 兴起之后,人们可能更多地使用 Python 进行爬虫. 毕竟,Python 有许多封装好的库。但对于 Javaer,如果你觉得学习 Python 成本比较高的话,使用 Java 也是一个不错的选择,尤其是当你希望在客户端进行爬虫的时候。
在这篇文中我们会以几个页面爬取的小例子来介绍使用 Java 进行爬虫的几种常用的手段。
1、使用 Jsoup 整理你的 Github
也许有许多人会像我一样,喜欢 Star 各种有趣的项目,希望某天用到的时候再看。但当你 Star 的项目太多的时候,想要再寻找之前的某个项目就会比较困难。因为寻找项目的时候必须在自己的 Star 列表中一页一页地去翻(国内访问 Github 的速度还比较慢)。你也可以用笔记整理下自己 Star 的项目,但这种重复性的工作,超过三次,就应该考虑用程序来解决了。此外,我们希望寻找自己 Star 过的项目的时候能够像检索数据库记录一样一个 SQL 搞定。所以,我们可以直接爬取 Star 列表并存储到本地数据库,然后我们就可以对这些数据为所欲为了不是?
下面我们开始进行页面信息的抓取。
1.1 页面分析
抓取页面信息之前我们首先要做的是分析页面的构成。我们可以使用 Chrome 来帮助我们解决这个问题。方式很简单,打开 Chrome,登入自己的 Github,然后点击页面的 Star 的 Tab 即可。然后,我们在自己的 Star 列表的一个条目上面进行 “检查” 即可,
如图所示,页面的一个 <div> 标签就对应了列表中的一个元素。我们可以使用 Jsoup 直接抓取到这个标签,然后对标签的子元素信息进行提取。这样就可以将整个列表中的信息全部检索出来。
页面分析的另一个问题是页面的自动切换,即,因为 Star 列表是分页的,一个页面信息加载完毕之后我们需要让程序自动跳转到下一页进行加载。对于 Github,这个要求是很容易满足的。因为 Github 的 Star 页面完全由服务端渲染完毕之后返回的,所以我们可以在页面中直接找到下一页的链接。
如图所示,我们直接在页面的 Next 按钮上面进行 “检查” 就可以看到,Next 按钮是一个 <a> 标签,这里直接包含了下一个页面的链接。
1.2 页面分析工具库
页面信息的分析,我们使用 Jsoup 来搞定。Jsoup 是一个基于 Java 的 HTML 解析器。我们可以直接通过在 pom.xml 中引入依赖来使用它,
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
这里我们使用 Maven 来作为项目的构建工具。如果你希望使用 Gradle 的话,稍微做下转换也是可以的。对于 Jsoup 的使用方式,你可以在其官方网站中进行了解:https://jsoup.org/.
1.3 配置数据库
然后,我们需要考虑的是数据的存储的问题。如果爬虫的数据量比较大、对数据库性能要求比较高的话,你可以使用 MySQL 和数据库连接池来提升读写性能。这里我们使用一种简单、轻量的数据库 H2. H2 是一个小型嵌入式数据库,它开源、纯java实现,是关系数据库,小巧且方便,非常适合我们的应用场景。
参考下面的步骤进行安装:在windows上安装H2数据库。
然后按照说明的方式打开即可。如果打开的时候发生了错误,需要检查下是否是端口被占用的问题。可以使用 H2 Console (Command line) 来打开。如果确实是因为端口占用的问题,参考下面的步骤结束占用端口的程序即可,如何查看某个端口被谁占用。
1.4 编码
这里我们使用 IDEA 作为开发工具,Maven 作为构建工具。
首先,我们需要引入项目所需的各种依赖。上面我们已经介绍了 Jsoup,为了在项目中使用 H2 数据库,我们还要引入 H2 的数据库驱动,
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
</dependency>
数据库的读写有许多封装的库,比如常用的 ORM 框架,Hibernate 和 Mybatis 等。这里我们使用原生的数据库读取方式,因为我们的项目比较小并且熟悉这些底层的东西更有益于我们学习和理解上述框架。所以,目前为止,我们需要引用的依赖总计就两个。
然后就是编写代码了。这里,我们首先考虑项目整体的结构。我们没有直接使用消费者生产者模式,而是创建一个由 6 条线程组成的线程池,其中 1 个线程用来做观察,另外 5 条线程执行任务。这 5 条线程会从一个线程安全的队列中取出需要解析的页面链接,并且当它们解析完毕之后会获取到下一个页面的链接并插入到列表中。另外,我们建立了一个对象 Repository 用来描述一个项目。于是代码如下,
// 线程池
private static ExecutorService executorService = Executors.newFixedThreadPool(6);
// 页面链接
private static BlockingQueue<String> pages = new ArrayBlockingQueue<>(10);
// 解析历史信息
private static List<String> histories = Collections.synchronizedList(new LinkedList<>());
// 解析出的项目记录
private static List<Repository> repositories = Collections.synchronizedList(new LinkedList<>());
// 布尔类型,用来标记是否解析完最后一页
private static AtomicBoolean lastPageParsed = new AtomicBoolean(false);
public static void main(String...args) {
// 启动监控线程
executorService.execute(new Watcher());
// 启动解析线程
executorService.execute(new Parser

最低0.47元/天 解锁文章
475

被折叠的 条评论
为什么被折叠?



