前两天,百度紧随GPT-4
发布了自己的语言模型文心一言。
讲道理,对于国内能够发布这样一个敢于对标CHAT GPT
的高质量语言模型,大家应该更多感受到的是赛博朋克与现实生活贴近的真实感,对这个模型应该有着更多的鼓励或赞美。
可不知是因为整个发布会搞的过于像没有好好准备的学生毕业答辩PPT,还是它的实际表现并没有那么如人意,大家貌似对文心一言并不那么买账。
于是我决定看一下知乎大神们对文心一言的评价,哪想到随便打开一个问题,居然有600多条回答…
要是我这一条一条翻完所有回答,估计就得拿出一天来全职摸鱼了,那么有没有什么办法能够最快的分析出对待这个问题大家的综合评价呢?
那么今天就让我纱布擦屁股,给大家露一小手,写一个爬虫扒下来所有的回答,再对结果进行一下分析。
WebMagic
正式开始前,咱们得先搞定工具。虽然python写起爬虫来有天然的框架优势,不过鉴于大家都是搞java的,那么我们今天就用java框架来实现一个爬虫。
咱们要使用的工具 WebMagic,就是一款简单灵活的java爬虫框架,总体架构由下面这几部分构成:
Downloader
:负责从互联网上下载页面,以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。PageProcessor
:负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。Scheduler
:负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。Pipeline
:负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了输出到控制台和保存到文件两种结果处理方案。
在4个主要组件中,除了PageProcessor
之外,其他3个组件基本都可以复用。而我们实际爬虫中的重点,就是要针对不同网页进行页面元素的分析,进而定制化地开发不同的PageProcessor
。
下面我们开始准备实战,先引入webmagic
的core
和extension
两个依赖,最新0.8.0
版本搞里头:
<dependency> <groupId>us.codecraft</groupId> <artifactId>webmagic-core</artifactId> <version>0.8.0</version> </dependency> <dependency> <groupId>us.codecraft</groupId> <artifactId>webmagic-extension</artifactId> <version>0.8.0</version> </dependency>
PageProcessor 与 xpath
在正式开始抓取页面前,我们先看看知乎上一个问题的页面是怎么构成的,还是以上面图中那个问题为例,原问题的地址在这里:
我们先做个简单的测试,来获取这个问题的标题 ,以及对这个问题的描述 。
通过浏览器的审查元素,可以看到标题是一个h1
的标题元素,并且它的class属性是QuestionHeader-title
,而问题的描述部分在一个div
中,它的class中包含了QuestionRichText
。
简单分析完了,按照前面说的,我们要对这个页面定制一个PageProcessor
组件抽取信息,直接上代码。
新建一个类实现PageProcessor
接口,并实现接口中的process()
这个方法即可。
public class WenxinProcessor implements PageProcessor { private Site site = Site.me() .setRetryTimes(3).setSleepTime(1000); @Override public void process(Page page) { String title = page.getHtml() .xpath("//h1[@class='QuestionHeader-title']/text()").toString(); String question= page.getHtml() .xpath("//div[@class='QuestionRichText']//tidyText()").toString(); System.out.println(title); System.out.println(question); } public Site getSite() { return site; } public static void main(String[] args) { Spider.create(new WenxinProcessor()) .addUrl("https://www.zhihu.com/question/589929380") .thread(2) .run(); } }
查看运行结果:
可以看到,在代码中通过xpath()
这样一个方法,成功拿到了我们要取的两个元素。其实说白了,这个xpath也不是爬虫框架中才有的新玩意,而是一种XML路径语言(XML Path Language
),是一种用来确定XML文档中某部分位置的语言。它基于XML的树状结构,提供在数据结构树中找寻节点的能力。
常用的路径表达式包括:
表达式 | 描述 |
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
在上面的代码中,//h1[@class='QuestionHeader-title']
就表示选取一个类型为h1
的节点,并且它有一个class为QuestionHeader-title
的属性。
至于后面的text()
和tidyText()
方法,则是用于提取元素中的文本,这些函数不是标准xpath中的,而是webMagic中特有的新方法,这些函数的使用可以参考文档:
http://webmagic.io/docs/zh/posts/ch4-basic-page-processor/xsoup.html
看到这,你可能还有个问题,这里对于问题的描述部分没有显示完全,你需要在页面上点一下这个显示全部它才会显示详细的信息。
没关系,这里先留个坑,这个问题放在后面解决。
获取提问的答案
我们完善一下上面的代码,尝试获取问题的解答。按照老套路,还是先分析页面元素再用xpath写表达式获取。修改process
方法:
@Override public void process(Page page) { String contentPath= "div[@class='QuestionAnswers-answers']"+ "//div[@class='RichContent RichContent--unescapable']" + "//div[@class='RichContent-inner']"+ "/tidyText()"; List<String> answerList = page.getHtml().xpath(contentPath).all(); for (int i = 0; i < answerList.size(); i++) { System.out.println("第"+(i+1)+"条回答:"); System.out.println(answerList.get(i)+"\n======="); } }
在上面的代码中,使用了xpath
获取页面中具有相同属性的元素,并将它们存入了List列表中。看一下运行结果:
纳尼?这个问题明明有着689条的回答,为什么我们只爬到了两条答案?
如果你经常用知乎来学习摸鱼的话,其实就会知道对于这种有大量回答的问题,页面刚开始只会默认显示很少的几条的消息,随着你不断的下拉页面才会把新的回答显示出来。
那么如果我想拿到所有的评论应该怎么做呢?这时候就要引出webMagic中另一个神奇的组件Selenium了。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
Selenium
简单来说,selenium是一个用于Web应用程序测试的工具,selenium测试可以直接运行在浏览器中,就像真正的用户在操作一样,并且目前主流的大牌浏览器一般都支持这项技术。
所以在爬虫中,我们可以通过编写模仿用户操作的selenium脚本,模拟进行一部分用互操作,比如点击事件或屏幕滚动等等。
WebMagic-Selenium需要依赖于WebDriver,所以我们先进行本地WebDriver的安装操作。
安装WebDriver
查看自己电脑上Chrome版本,可以点击设置
->关于chrome
查看,也可以直接在地址栏输入chrome://settings/help
:
可以看到版本号,然后需要下载对应版本的WebDriver,下载地址:
打开后,可以看到各个版本,选择与本地浏览器最接近的版本:
点击进入后,根据我们的系统选择对应版本下载即可。
下载完成后,解压到本地目录中,之后在使用selenium模块中会使用到。这个文件建议放在chrome的安装目录下,否则之后在代码中可能会报一个WebDriverException: unknown error: cannot find Chrome binary
找不到chrome文件的错误。