集搜客GooSeeker网络爬虫

标题: Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容 [打印本页]

作者: gz51837844    时间: 2016-4-29 16:06
标题: Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容
1,引言

Python网络爬虫内容提取器一文我们详细讲解了核心部件:可插拔的内容提取器类gsExtractor。本文记录了确定gsExtractor的技术路线过程中所做的编程实验。这是第二部分,第一部分实验了用xslt方式一次性提取静态网页内容并转换成xml格式。留下了一个问题:javascript管理的动态内容怎样提取?那么本文就回答这个问题。


2,提取动态内容的技术部件

在上一篇python使用xslt提取网页数据中,要提取的内容是直接从网页的source code里拿到的。但是一些Ajax动态内容是在source code找不到的,就要找合适的程序库把异步或动态加载的内容加载上来,交给本项目的提取器进行提取。

python可以使用selenium执行javascript,selenium可以让浏览器自动加载页面,获取需要的数据。selenium自己不带浏览器,可以使用第三方浏览器如Firefox,Chrome等,也可以使用headless浏览器如PhantomJS在后台执行。

3,源代码和实验过程

假如我们要抓取京东手机页面的手机名称和价格(价格在网页源码是找不到的),如下图:
[attach]816[/attach]


第一步:利用集搜客谋数台的直观标注功能,可以极快速度自动生成一个调试好的抓取规则,其实是一个标准的xslt程序,如下图,把生成的xslt程序拷贝到下面的程序中即可。注意:本文只是记录实验过程,实际系统中,将采用多种方式把xslt程序注入到内容提取器重。
[attach]817[/attach]


第二步:执行如下代码(在windows10, python3.2下测试通过,源代码下载地址请见文章末尾GitHub),请注意:xslt是一个比较长的字符串,如果删除这个字符串,代码没有几行,足以见得Python之强大
  1. #/usr/bin/python
  2. # 2016.04.29 版本1.0
  3. # 2017.07.29 版本2.0
  4. from urllib import request
  5. from lxml import etree
  6. from selenium import webdriver
  7. import time

  8. # 京东手机商品页面
  9. url = "http://item.jd.com/4325123.html"

  10. # 下面的xslt是通过集搜客的谋数台图形界面自动生成的
  11. xslt_root = etree.XML("""\
  12. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  13. <xsl:template match="/">
  14. <li>
  15. <xsl:apply-templates select="//*[@class='sku-name' and count(././text())>0]" mode="li"/>
  16. </li>
  17. </xsl:template>


  18. <xsl:template match="//*[@class='sku-name' and count(././text())>0]" mode="li">
  19. <item>
  20. <商品名称>
  21. <xsl:value-of select="./text()"/>
  22. </商品名称>
  23. <京东价>
  24. <xsl:value-of select="following-sibling::div[position()=2]//*[@class='p-price']/span[position()=2]/text()"/>
  25. </京东价>
  26. </item>
  27. </xsl:template>
  28. </xsl:stylesheet>""")

  29. # 使用webdriver.PhantomJS
  30. browser = webdriver.PhantomJS(executable_path='C:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe')
  31. browser.get(url)
  32. time.sleep(3)

  33. transform = etree.XSLT(xslt_root)

  34. # 执行js得到整个dom
  35. html = browser.execute_script("return document.documentElement.outerHTML")
  36. doc = etree.HTML(html)
  37. # 用xslt从dom中提取需要的字段
  38. result_tree = transform(doc)
  39. print(result_tree)
复制代码

第三步:下图可以看到,网页中的手机名称和价格被正确抓取下来了
[attach]818[/attach]

4,接下来阅读

至此,我们通过两篇文章演示怎样抓取静态和动态网页内容,都采用了xslt一次性将需要的内容从网页上提取出来,其实xslt是一个比较复杂的程序语言,如果手工编写xslt,那么还不如写成离散的xpath。如果这个xslt不是手工写出来的,而是程序自动生成的,这就有意义了,程序员再也不要花时间编写和调测抓取规则了,这是很费时费力的工作。下一篇《1分钟快速生成用于网页内容提取的xslt》将讲述怎样生成xslt。

5,集搜客GooSeeker开源代码下载源

1. GooSeeker开源Python网络爬虫GitHub源

6,文档修改历史
2016-05-26:V2.0,增补文字说明
2016-05-29:V2.1,增加第五章:源代码下载源,并更换github源的网址





作者: Fuller    时间: 2016-4-29 17:05
原来PhantomJS类似于一个无头浏览器啊,不知道他的适用范围广不广。如果用selenium驱动Firefox或者Chrome的无头浏览器会怎样?是否也很容易用?
作者: Fuller    时间: 2016-5-1 16:04
DS打数机能够自动滚屏、连续动作和模拟点击,不知道这个框架能否实现
作者: Fuller    时间: 2016-5-29 15:23
在简书上看到这么一篇文章《【Python】爬虫技术:(JavaScript渲染)动态页面抓取超级指南》,用Qt web kit库。我很纳闷,这篇文章和上文都是把document变成字符串以后,交给lxml的etree再解析成dom树,然后用xpath或者xslt进行转换。这样岂不多了一道?难道是我理解错了?
PhantomJS为什么要返回outerHTML?为什么不直接用documentElement?

作者: zxy0427    时间: 2016-5-30 20:39
执行lxml的etree.HTML(), 是把PhantomJS返回的documentElement.outerHTML转换成特定格式的element tree
实测documentElement.outerHTML返回值是整个DOM, 如果取documentElement的返回值,则只拿到下面的这串:
<selenium.webdriver.remote.webelement.WebElement (session="6912b3f0-2663-11e6-9999-b118726d63d1", element=":wdc:1464611912251")>
作者: Fuller    时间: 2016-5-30 21:40
zxy0427 发表于 2016-5-30 20:39
执行lxml的etree.HTML(), 是把PhantomJS返回的documentElement.outerHTML转换成特定格式的element tree
实 ...

Python我是刚刚学,这里我糊涂了,但是我仔细看过DOM国际规范。我有下面的猜测:
1,outerHTML似乎返回的是一个字符串
2,用etree.HTML()把这个字符串转换成了elementtree对象,这个tree对象能用etree的xpath进行定位,那么很可能这个tree对象不是标准的documentElement对象,而是仿DOM的对象结构
3,你的测试结果那么奇怪,会不会是python内部的对象id?
4,如果selenium能够返回一个标准的documentElement对象,那么Python有没有标准的DOM访问接口?比如,getElementsByTag(), getElementById()之类的
5,如果支持标准DOM,是不是就不用转成element tree了?
作者: shenzhenwan10    时间: 2016-5-31 13:59
本帖最后由 shenzhenwan10 于 2016-5-31 16:41 编辑

我理解etree.HTML(), etree.XML()这几个方法就是用python的数据结构建立一棵树,用于之后对etree的操作
目前python库实现了xslt提取的只找到了lxml的etree,实现了xpath提取的倒是不少,如BS4, HTML parser

作者: shenzhenwan10    时间: 2016-5-31 16:42
如果用selenium.webdriver来直接操作浏览器,那就不用转换了。
比如下面的几行代码:
  1. driver = webdriver.Firefox("C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe")
  2. driver.get("http://passport.baidu.com")
  3. time.sleep(10)
  4. driver.find_element_by_id("TANGRAM__PSP_3__userName").click()
  5. driver.find_element_by_id("TANGRAM__PSP_3__userName").send_keys("dali02310")
复制代码

作者: Fuller    时间: 2016-5-31 23:28
shenzhenwan10 发表于 2016-5-31 16:42
如果用selenium.webdriver来直接操作浏览器,那就不用转换了。
比如下面的几行代码:
...

能否简单解释一下上面这段代码是干什么用的?那个网址是哪里?为什么要这么做?

作者: Fuller    时间: 2016-5-31 23:37
shenzhenwan10 发表于 2016-5-31 13:59
我理解etree.HTML(), etree.XML()这几个方法就是用python的数据结构建立一棵树,用于之后对etree的操作
目 ...

我看到一段别人写的代码,用Qt webkit 的driver,刚才还看到你的跟帖用Firefox的driver。我一直怀疑这样做的能力到底有多强。原因分析如下:

无论用.net编程工具直接操作IE或者Firefox的nsBrowser,还是你这些代码,以及webkit driver,你都可以想象成用一个编程语言操作一个黑盒子,你能控制这个黑盒子的能力局限在这个盒子提供的接口能力。从面向对象技术来说,这种接口协约确保了安全性和一致性,这是来自于“约束”的。

如果另一条路线:我直接进入到这个盒子里面,嵌入我的代码,把这个盒子的其它功能作为我的四肢,我的代码指挥他们,就可以不受接口的约束。GooSeeker网络爬虫DS打数机就是用这个思路做成的,他可以模拟人的任何操作浏览器的行为。

也许是我对python理解不够,权当抛砖引玉

作者: Fuller    时间: 2016-6-4 09:48
我们得用上非阻塞式http client
作者: Fuller    时间: 2016-6-8 18:53
Python好热,好多高人啊,看到一个好棒的代码分享:基于微信网页版的微信机器人python实现
作者: Fuller    时间: 2016-6-8 18:56
凡是机器人(web bot)程序,我都很喜欢,比如这个,python3+任务计划实现的人人影视网站自动签到
作者: victorray    时间: 2016-8-31 16:46
价格只是抓了一个啊 怎么能把所有型号的都抓到
作者: shenzhenwan10    时间: 2016-9-2 10:31
这篇文章是测试,你可以看看内容提取器及几篇实战文章,里面的步骤很详细
作者: mingdongtianxia    时间: 2017-7-29 11:16
按教程操作下来怎么只有个开头和一个结尾标签是怎么回事,几乎所有的都如此,你们把API给关了还是停止服务了?
作者: mingdongtianxia    时间: 2017-7-29 11:18
Fuller 发表于 2016-6-8 18:53
Python好热,好多高人啊,看到一个好棒的代码分享:基于微信网页版的微信机器人python实现 ...

大神啊,你还在么?最近不见你动态了呢?

作者: shenzhenwan10    时间: 2017-7-29 16:10
mingdongtianxia 发表于 2017-7-29 11:16
按教程操作下来怎么只有个开头和一个结尾标签是怎么回事,几乎所有的都如此,你们把API给关了还是停止服务 ...

api没有关,有可能是提取器失效
周末这2天技术会测试一下



作者: gz51837844    时间: 2017-7-29 23:57
今天按这个主题的步骤,做了测试,结果如下:
1. 这篇文章是手工把提取器内容从MS谋数台复制到python程序文件中,没有使用API
2. 原文中测试所用的商品网址,该商品已经下架。这个在测试时应该检查并替换成有效的网址
3. 京东的商品详情页面结构有变化,原提取器内容无法提取到商品名称和价格,需要在MS谋数台里做好规则后,把规则复制到程序代码中
[attach]7108[/attach]

在此上传已更新的实测所用的程序文件,可以参考
[attach]7109[/attach]

  1. #/usr/bin/python
  2. from urllib import request
  3. from lxml import etree
  4. from selenium import webdriver
  5. import time

  6. # 京东手机商品页面
  7. url = "http://item.jd.com/4325123.html"

  8. # 下面的xslt是通过集搜客的谋数台图形界面自动生成的
  9. xslt_root = etree.XML("""\
  10. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  11. <xsl:template match="/">
  12. <li>
  13. <xsl:apply-templates select="//*[@class='sku-name' and count(././text())>0]" mode="li"/>
  14. </li>
  15. </xsl:template>


  16. <xsl:template match="//*[@class='sku-name' and count(././text())>0]" mode="li">
  17. <item>
  18. <商品名称>
  19. <xsl:value-of select="./text()"/>
  20. </商品名称>
  21. <京东价>
  22. <xsl:value-of select="following-sibling::div[position()=2]//*[@class='p-price']/span[position()=2]/text()"/>
  23. </京东价>
  24. </item>
  25. </xsl:template>
  26. </xsl:stylesheet>""")

  27. # 使用webdriver.PhantomJS
  28. browser = webdriver.PhantomJS(executable_path='C:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe')
  29. browser.get(url)
  30. time.sleep(3)

  31. transform = etree.XSLT(xslt_root)

  32. # 执行js得到整个dom
  33. html = browser.execute_script("return document.documentElement.outerHTML")
  34. doc = etree.HTML(html)
  35. # 用xslt从dom中提取需要的字段
  36. result_tree = transform(doc)
  37. print(result_tree)
复制代码




作者: mingdongtianxia    时间: 2017-7-31 20:48
gz51837844 发表于 2017-7-29 23:57
今天按这个主题的步骤,做了测试,结果如下:
1. 这篇文章是手工把提取器内容从MS谋数台复制到python程序文 ...

对于翻页怎么办呢?它只能采集单页啊,如果一共有5页怎么办呢?能否将MS谋数台的线索规则像数据规则一样能提取过来用呢?那样就好了呀,

作者: gz51837844    时间: 2017-7-31 21:02
没有做翻页部分, 需要用python去自己实现
作者: gilmechen    时间: 2017-12-1 16:22
怎么把爬虫路线的xslt加入到py脚本中呢,我需要实现翻页抓取lazyload的动态网页。现在在MS用滚屏动作和DS的延迟抓取都没有办法抓到网页上的所有条目,考虑写py脚本来实现。可以帮我看一下吗?示例网址:https://www.mercari.com/jp/searc ... mp;status_on_sale=1
作者: umsung    时间: 2017-12-1 16:34
本帖最后由 umsung 于 2017-12-1 16:44 编辑
gilmechen 发表于 2017-12-1 16:22
怎么把爬虫路线的xslt加入到py脚本中呢,我需要实现翻页抓取lazyload的动态网页。现在在MS用滚屏动作和DS的 ...

不需要用py脚本来实现,直接用爬虫软件制作规则就可以实现翻页抓取,需要用到样例复制翻页,采集的时候在ds打数机内开启滚屏设置,采集的时候就会加载出动态页面。在ds打数机中-配置-》滚屏参数中设置,把次数设置大于0就会滚屏 ,把滚屏速度调慢一点。

作者: gilmechen    时间: 2017-12-1 18:03
umsung 发表于 2017-12-1 16:34
不需要用py脚本来实现,直接用爬虫软件制作规则就可以实现翻页抓取,需要用到样例复制和翻页,采集的时候 ...

我把滚屏速度设成0(应该是最慢了吧)还是只能抓到十几条数据

作者: wangyong    时间: 2017-12-1 18:05
gilmechen 发表于 2017-12-1 18:03
我把滚屏速度设成0(应该是最慢了吧)还是只能抓到十几条数据

这种情况是要把滚屏次数设大,数字越大滚屏滚的越多

作者: gilmechen    时间: 2017-12-1 18:05
gilmechen 发表于 2017-12-1 18:03
我把滚屏速度设成0(应该是最慢了吧)还是只能抓到十几条数据

翻页我可以用集搜客实现,但是因为页面信息抓不全所以才考虑用python,然而用python的话又要写代码实现其他细节
作者: gilmechen    时间: 2017-12-1 18:07
wangyong 发表于 2017-12-1 18:05
这种情况是要把滚屏次数设大,数字越大滚屏滚的越多

滚屏次数设成50也没有明显效果

作者: umsung    时间: 2017-12-1 18:13
本帖最后由 umsung 于 2017-12-1 18:21 编辑

1,滚屏速度可以设置为可正可负的整数,-1、0和1相同,表示不变速。< -1 表示降低速度,> 1 表示提高速度。
2,把打数机-》配置中把延迟抓取和超时时长设置大一些
3,观察抓取不全的原因是因为滚屏动态加载而采集不全还是规则设置有问题,这种网页一般只要规则做对了,把滚屏次数设置大一些,滚屏速度设置慢一点就可以采集全。

4,因为这种动态加载页面是延迟加载的,要滚屏到那里才会加载,才能采集的到。所以在MS谋数台定义规则的时候,要先手工滚屏到底,看到内容节点都加载出来了,选择菜单 规则-》刷新页面结构,再开始做规则,点击测试的时候才能看到是否能采集完全。





欢迎光临 集搜客GooSeeker网络爬虫 (https://www.gooseeker.com/doc/) Powered by Discuz! X3.2