哈喽咱们好啊,我是Hydra。

前两天,百度紧随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

下面咱们开端预备实战,先引入webmagiccoreextension两个依靠,最新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中特有的新办法,这些函数的运用能够参阅文档:

webmagic.io/docs/zh/pos…

看到这,你或许还有个问题,这儿关于问题的描绘部分没有显现彻底,你需求在页面上点一下这个显现悉数它才会显现具体的信息。

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

没关系,这儿先留个坑,这个问题放在后边处理。

获取发问的答案

咱们完善一下上面的代码,测验获取问题的解答。按照老套路,仍是先剖析页面元素再用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了。

Selenium

简略来说,selenium是一个用于Web应用程序测验的东西,selenium测验能够直接运行在浏览器中,就像真实的用户在操作一样,而且现在干流的大牌浏览器一般都支持这项技术。

所以在爬虫中,咱们能够经过编写模仿用户操作的selenium脚本,模仿进行一部分用互操作,比如点击事情或屏幕翻滚等等。

WebMagic-Selenium需求依靠于WebDriver,所以咱们先进行本地WebDriver的安装操作。

安装WebDriver

检查自己电脑上Chrome版别,能够点击设置->关于chrome检查,也能够直接在地址栏输入chrome://settings/help

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

能够看到版别号,然后需求下载对应版其他WebDriver,下载地址:

chromedriver.storage.googleapis.com/index.html

翻开后,能够看到各个版别,挑选与本地浏览器最接近的版别:

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

点击进入后,依据咱们的体系挑选对应版别下载即可。

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

下载完成后,解压到本地目录中,之后在运用selenium模块中会运用到。这个文件主张放在chrome的安装目录下,不然之后在代码中或许会报一个WebDriverException: unknown error: cannot find Chrome binary找不到chrome文件的错误。

修正Selenium源码

webMagic中现已封装了selenium模块的代码,但官方版其他代码有些当地需求修正,咱们下载源码后要自己简略改动一下然后从头编译。我这下载了0.8.1-SNAPSHOT版其他代码,官方git地址:

github.com/code4craft/…

修正装备文件地址,在WebDriverPoolselenium装备文件途径写死了,需求改动装备途径:

// 修正前
// private static final String DEFAULT_CONFIG_FILE = "/data/webmagic/webmagic-selenium/config.ini";
// 修正后
private static final String DEFAULT_CONFIG_FILE = "selenium.properties";

resources目录下增加装备文件selenium.properties

# What WebDriver to use for the tests
driver=chrome
# PhantomJS specific config (change according to your installation)
chrome_driver_loglevel=DEBUG

js模仿页面操作

修正SeleniumDownloaderdownload()办法,在代码中的这个方位,作者很贴心的给咱们留了一行注释:

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

意思便是,你能够在这增加鼠标事情或许干点其他什么东西了。咱们在这增加页面向下翻滚这一模仿事情,每休眠2s就向下翻滚一下页面,一共下拉20次:

//模仿下拉,改写页面
for (int i=0; i < 20; i++){
    System.out.println("休眠2s");
    try {
        //翻滚到最底部
        ((JavascriptExecutor)webDriver)
                .executeScript("window.scrollTo(0,document.body.scrollHeight)");
        //休眠,等候加载页面
        Thread.sleep(2000);
        //往回滚一点,不然不加载
        ((JavascriptExecutor)webDriver)
                .executeScript("window.scrollBy(0,-300)");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

修正完成后本地打个包,注意还要修正一下版别号,改成和发行版的不同即可,我这儿改成了0.8.1.1-SNAPSHOT

mvn clean install

调用

回到之前的爬虫项目,引入咱们自己打好的包:

<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-selenium</artifactId>
    <version>0.8.1.1-SNAPSHOT</version>
</dependency>

修正之前的主程序启动时的代码,增加Downloader组件,SeleniumDownloader结构办法的参数中传入咱们下好的chrome的webDriver的可执行文件的地址:

public static void main(String[] args) {
    Spider.create(new WenxinProcessor())
            .addUrl("https://www.zhihu.com/question/589929380")
            .thread(2)
            .setDownloader(new SeleniumDownloader("D:\\Program Files\\Google\\Chrome\\Application\\chromedriver.exe")
                    .setSleepTime(1000))
            .run();
}

进行测验,能够看到在拉动了40秒窗口后,获取到的答案条数是100条:

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

经过适当地增加下拉页面的循环的次数,咱们就能够获取到当时问题下的悉数答复了。

别的,在启动爬虫后咱们会看到webDriver弹出了一个chrome的窗口,在这个窗口中有一个提示:Chrome正遭到自动测验软件的操控,而且能够看到页面不断的自动下拉情况:

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

假如不想要这个弹窗的话,能够修正selenium模块的代码进行躲藏。修正WebDriverPoolconfigure()办法,找到这段代码:

if (driver.equals(DRIVER_CHROME)) {
	mDriver = new ChromeDriver(sCaps);
}

增加一个躲藏显现的选项,而且在修正完成后,从头打包一下。

if (driver.equals(DRIVER_CHROME)) {
	ChromeOptions options=new ChromeOptions();
	options.setHeadless(true);
	mDriver = new ChromeDriver(options);
}

获取问题具体描绘

不知道咱们还记不记得在前面还留了一个坑,咱们现在获取到的对问题的描绘是不全的,需求点一下这个按钮才干显现彻底。

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

相同,这个问题也能够用selenium来处理,在咱们下拉页面前,加上这么一个模仿点击事情,就能够取得对问题的具体描绘了:

((JavascriptExecutor)webDriver)
  .executeScript("document.getElementsByClassName('Button QuestionRichText-more')[0].click()");

看一下执行成果,现已能够拿到完好内容了:

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

Pipeline

到这儿,尽管要爬的数据获取到了,可是要进行剖析的话,还需求进行耐久化操作。在前面的webMagic的架构图中,介绍过Pipeline组件首要担任成果的处理,所以咱们再优化一下代码,增加一个Pipeline担任数据的耐久化。

由于数据量也不是非常大,这儿我挑选了直接存入ElasticSearch中,同时也便利咱们进行后续的剖析操作,ES组件我运用的是esclientrhl,为了便利我仍是把项目整个扔到了 spring里边。

定制一个Pipeline也很简略,完成Pipeline接口并完成里边的process()接口就能够了,经过结构办法传入ES耐久化层组件:

@Slf4j
@AllArgsConstructor
public class WenxinPipeline implements Pipeline {
    private final ZhihuRepository zhihuRepository;
    @Override
    public void process(ResultItems resultItems, Task task) {
        Map<String, Object> map = resultItems.getAll();
        String title = map.get("title").toString();
        String question = map.get("question").toString();
        List<String> answer = (List<String>) map.get("answer");
        ZhihuEntity zhihuEntity;
        for (String an : answer) {
            zhihuEntity = new ZhihuEntity();
            zhihuEntity.setTitle(title);
            zhihuEntity.setQuestion(question);
            zhihuEntity.setAnswer(an);
            try {
                zhihuRepository.save(zhihuEntity);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

把selenium向下拉取页面的次数改成200后,经过接口启动程序:

@GetMapping("wenxin")
public void wenxin() {
    new Thread(() -> {
        Request request = new Request("https://www.zhihu.com/question/589929380");
        WenxinProcessor4 wenxinProcessor = new WenxinProcessor4();
        Spider.create(wenxinProcessor)
                .addRequest(request)
                .addPipeline(new WenxinPipeline(zhihuRepository))
                .setDownloader(new SeleniumDownloader("D:\\Program Files\\Google\\Chrome\\Application\\chromedriver.exe")
                        .setSleepTime(1000))
                .run();
    }).start();
}

运行完成后,查询一下es中的数据,能够看到,实践爬取到了673条答复。

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

别的,咱们能够在一个爬虫程序中传入多个页面地址,只要页面元素具有相同的规矩,那么它们就能用相同的爬虫逻辑处理,在下面的代码中,咱们一次性传入多个页面:

Spider.create(new WenxinProcessor4())
        .addUrl(new String[]{"https://www.zhihu.com/question/589941496",
              "https://www.zhihu.com/question/589904230","https://www.zhihu.com/question/589938328"})
        .addPipeline(new WenxinPipeline(zhihuRepository))
        .setDownloader(new SeleniumDownloader("D:\\Program Files\\Google\\Chrome\\Application\\chromedriver.exe")
                .setSleepTime(1000))
        .run();

一顿忙活下来,终究扒下来1300多条数据。

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

剖析

数据落到了ES里后,那咱们就能够依据关键字进行剖析了,咱们先挑选10个负面方向的词语进行查询,能够看到查到了403条数据,将近占到了总量的三分之一。

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

再从各种答复中挑选10个正向词语查询,成果大概只要负面方向的一半左右:

扒了一千多条关于文心一言的评论,我发现这届网友真的不好糊弄…

不得不说,这届网友真的是很严厉…

Proxy署理

说到爬虫,其实还有一个绕不过去的东西,那便是署理。

像咱们这样的小打小闹,爬个百八十条数据尽管没啥问题,可是假如要去爬取很多数据或是用于商业,仍是主张运用一下署理,一方面能够躲藏咱们的IP地址起到保护自己的效果,另一方面动态IP也能有效的应对一些反爬策略。

个人在运用中,比较推荐的是地道署理。简略的来说,假如你购买了IP服务的话,用普通署理方法的话需求你去手动恳求接口获取IP地址,再到代码中动态修正。而运用地道署理的话,就不需求自己提取署理IP了,每条地道自动提取并运用署理IP转发用户恳求,这样咱们就能够专注于业务了。

尽管网上也有免费的署理能够能用,但要不然便是失效的太快,要不便是很简单被网站参与黑名单,所以假如寻求性能的话仍是买个专业点的署理比较好,尽管或许价格不那么便宜便是了。

题外话

看了一大顿下来,从咱们略显尖锐的言辞来看,咱们总体上对文心一言仍是不那么满足的。究竟,在有着CHAT-GPT这么一个优秀的产品做布景板的前提下,这届网友或许没有那么好糊弄。

可是话又说回来,丑媳妇总得见公婆不是?提早露出缺点,也有利于国内的这些大厂,看清和一流AI产品之间的真实距离,知难而进,迎头赶上。

那么,这次的分享就到这儿,我是Hydra,咱们下篇再会。

文中代码现已传到我的git上啦,github.com/trunks2008/… ,欢迎咱们来个star鼓舞一下~

参阅资料:

webmagic.io/docs/zh/pos…

blog.csdn.net/panchang199…


作者简介,码农参上,一个热爱分享的公众号,风趣、深化、直接,与你聊聊技术。也欢迎增加我老友,多多沟通。


本文正在参与「金石方案」