1,引言
在Python网络爬虫内容提取器一文我们详细讲解了核心部件:可插拔的内容提取器类gsExtractor。本文记录了确定gsExtractor的技术路线过程中所做的编程实验。这是第一部分,实验了用xslt方式一次性提取静态网页内容并转换成xml格式。

2,用lxml库实现网页内容提取

lxml是python的一个库,可以迅速、灵活地处理 XML。它支持 XML Path Language (XPath) 和 Extensible Stylesheet Language Transformation (XSLT),并且实现了常见的 ElementTree API。

这2天测试了在python中通过xslt来提取网页内容,记录如下:

2.1,抓取目标

假设要提取集搜客官网旧版论坛的帖子标题和回复数,如下图,要把整个列表提取出来,存成xml格式

集搜客旧版论坛.jpg

2.2,源代码1:只抓当前页,结果显示在控制台

Python的优势是用很少量代码就能解决一个问题,请注意下面的代码看起来很长,其实python函数调用没有几个,大篇幅被一个xslt脚本占去了,在这段代码中,只是一个好长的字符串而已,至于为什么选择xslt,而不是离散的xpath或者让人挠头的正则表达式,请参看《Python即时网络爬虫项目启动说明》,我们期望通过这个架构,把程序员的时间节省下来一大半。

可以拷贝运行下面的代码(在windows10, python3.2下测试通过):
  1. from urllib import request
  2. from lxml import etree
  3. url="http://www.gooseeker.com/cn/forum/7"
  4. conn = request.urlopen(url)
  5. doc = etree.HTML(conn.read())
  6. xslt_root = etree.XML("""\
  7. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  8. <xsl:template match="/">
  9. <列表>
  10. <xsl:apply-templates select="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表"/>
  11. </列表>
  12. </xsl:template>
  13. <xsl:template match="table/tbody/tr[position()>=1]" mode="list">
  14. <item>
  15. <标题>
  16. <xsl:value-of select="*//*[@class='topic']/a/text()"/>
  17. <xsl:value-of select="*[@class='topic']/a/text()"/>
  18. <xsl:if test="@class='topic'">
  19. <xsl:value-of select="a/text()"/>
  20. </xsl:if>
  21. </标题>
  22. <回复数>
  23. <xsl:value-of select="*//*[@class='replies']/text()"/>
  24. <xsl:value-of select="*[@class='replies']/text()"/>
  25. <xsl:if test="@class='replies'">
  26. <xsl:value-of select="text()"/>
  27. </xsl:if>
  28. </回复数>
  29. </item>
  30. </xsl:template>
  31. <xsl:template match="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表">
  32. <item>
  33. <list>
  34. <xsl:apply-templates select="table/tbody/tr[position()>=1]" mode="list"/>
  35. </list>
  36. </item>
  37. </xsl:template>
  38. </xsl:stylesheet>""")
  39. transform = etree.XSLT(xslt_root)
  40. result_tree = transform(doc)
  41. print(result_tree)
复制代码
源代码请通过本文结尾的GitHub源下载。

2.3,抓取结果

得到的抓取结果如下图:
python_xslt_运行结果.jpg

2.4,源代码2:翻页抓取,结果存入文件


我们对2.2的代码再做进一步修改,增加翻页抓取和存结果文件功能,代码如下:
  1. from urllib import request
  2. from lxml import etree
  3. import time
  4. xslt_root = etree.XML("""\
  5. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  6. <xsl:template match="/">
  7. <列表>
  8. <xsl:apply-templates select="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表"/>
  9. </列表>
  10. </xsl:template>
  11. <xsl:template match="table/tbody/tr[position()>=1]" mode="list">
  12. <item>
  13. <标题>
  14. <xsl:value-of select="*//*[@class='topic']/a/text()"/>
  15. <xsl:value-of select="*[@class='topic']/a/text()"/>
  16. <xsl:if test="@class='topic'">
  17. <xsl:value-of select="a/text()"/>
  18. </xsl:if>
  19. </标题>
  20. <回复数>
  21. <xsl:value-of select="*//*[@class='replies']/text()"/>
  22. <xsl:value-of select="*[@class='replies']/text()"/>
  23. <xsl:if test="@class='replies'">
  24. <xsl:value-of select="text()"/>
  25. </xsl:if>
  26. </回复数>
  27. </item>
  28. </xsl:template>
  29. <xsl:template match="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表">
  30. <item>
  31. <list>
  32. <xsl:apply-templates select="table/tbody/tr[position()>=1]" mode="list"/>
  33. </list>
  34. </item>
  35. </xsl:template>
  36. </xsl:stylesheet>""")
  37. baseurl = "http://www.gooseeker.com/cn/forum/7"
  38. basefilebegin = "jsk_bbs_"
  39. basefileend = ".xml"
  40. count = 1
  41. while (count < 12):
  42.         url = baseurl + "?page=" + str(count)
  43.         conn = request.urlopen(url)
  44.         doc = etree.HTML(conn.read())
  45.         transform = etree.XSLT(xslt_root)
  46.         result_tree = transform(doc)
  47.         print(str(result_tree))
  48.         file_obj = open(basefilebegin+str(count)+basefileend,'w',encoding='UTF-8')
  49.         file_obj.write(str(result_tree))
  50.         file_obj.close()
  51.         count += 1
  52.         time.sleep(2)
复制代码
我们增加了写文件的代码,还增加了一个循环,构造每个翻页的网址,但是,如果翻页过程中网址总是不变怎么办?其实这就是动态网页内容,下面会讨论这个问题。

3,总结

这是开源Python通用爬虫项目的验证过程,在一个爬虫框架里面,其它部分都容易做成通用的,就是网页内容提取和转换成结构化的操作难于通用,我们称之为提取器。但是,借助GooSeeker可视化提取规则生成器MS谋数台 ,提取器的生成过程将变得很便捷,而且可以标准化插入,从而实现通用爬虫,在后续的文章中会专门讲解MS谋数台与Python配合的具体方法。

4,接下来阅读


本文介绍的方法通常用来抓取静态网页内容,也就是所谓的html文档中的内容,目前很多网站内容是用javascript动态生成的,一开始html是没有这些内容的,通过后加载方式添加进来,那么就需要采用动态技术,请阅读《Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容

5,集搜客GooSeeker开源代码下载源
1. GooSeeker开源Python网络爬虫GitHub源

6,文档修改历史
2016-05-26:V2.0,增补文字说明;把跟帖的代码补充了进来

2016-05-29:V2.1,增加最后一章源代码下载源









举报 使用道具
| 回复

共 13 个关于本帖的回复 最后回复于 2018-8-23 18:01

Fuller 管理员 发表于 2016-4-26 15:42:20 | 显示全部楼层
这是一个很好的经验共享,更多的python爬虫经验是用正则表达式,但是正则表达式的编写和测试特别麻烦,如果用xslt,有一种一气呵成的感觉,直接使用MS谋数台自动生成的抓取规则,做成一个实时抓取系统。
举报 使用道具
Fuller 管理员 发表于 2016-4-26 23:39:25 | 显示全部楼层
把这个程序做完善以后开源吧
举报 使用道具
gz51837844 管理员 发表于 2016-4-27 12:54:22 | 显示全部楼层
修改了一下,增加了:1.翻页 2.抓取结果写入文件
更新后的代码如下:
  1. from urllib import request
  2. from lxml import etree
  3. import time

  4. xslt_root = etree.XML("""\
  5. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  6. <xsl:template match="/">
  7. <列表>
  8. <xsl:apply-templates select="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表"/>
  9. </列表>
  10. </xsl:template>



  11. <xsl:template match="table/tbody/tr[position()>=1]" mode="list">
  12. <item>
  13. <标题>
  14. <xsl:value-of select="*//*[@class='topic']/a/text()"/>
  15. <xsl:value-of select="*[@class='topic']/a/text()"/>
  16. <xsl:if test="@class='topic'">
  17. <xsl:value-of select="a/text()"/>
  18. </xsl:if>
  19. </标题>
  20. <回复数>
  21. <xsl:value-of select="*//*[@class='replies']/text()"/>
  22. <xsl:value-of select="*[@class='replies']/text()"/>
  23. <xsl:if test="@class='replies'">
  24. <xsl:value-of select="text()"/>
  25. </xsl:if>
  26. </回复数>
  27. </item>
  28. </xsl:template>

  29. <xsl:template match="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表">
  30. <item>
  31. <list>
  32. <xsl:apply-templates select="table/tbody/tr[position()>=1]" mode="list"/>
  33. </list>
  34. </item>
  35. </xsl:template>
  36. </xsl:stylesheet>""")

  37. baseurl="http://www.gooseeker.com/cn/forum/7"
  38. basefilebegin="jsk_bbs_"
  39. basefileend=".xml"
  40. count=1
  41. while (count < 12):
  42.         url=baseurl + "?page=" + str(count)
  43.         conn=request.urlopen(url)
  44.         doc = etree.HTML(conn.read())
  45.         transform = etree.XSLT(xslt_root)
  46.         result_tree = transform(doc)
  47.         print(str(result_tree))
  48.         file_obj=open(basefilebegin+str(count)+basefileend,'w',encoding='UTF-8')
  49.         file_obj.write(str(result_tree))
  50.         file_obj.close()
  51.         count+=1
  52.         time.sleep(2)
复制代码



举报 使用道具
Fuller 管理员 发表于 2016-5-1 16:03:41 | 显示全部楼层
python网络数据采集  这本书不错,好像这本书上面主要讲正则表达式方法,正则表达式那么难调试,但是还是有好多人使用
举报 使用道具
Fuller 管理员 发表于 2016-6-2 22:03:36 | 显示全部楼层
这里有一篇用mechanize模拟登录的文章:http://www.jianshu.com/p/5fb833566d76
举报 使用道具
comboo 新手上路 发表于 2016-6-14 10:15:50 | 显示全部楼层
没有看出来哪里方便了
select="*//*[@class='topic']/a/text()"  这不是还是xpath
举报 使用道具
Fuller 管理员 发表于 2016-6-14 11:20:51 | 显示全部楼层
comboo 发表于 2016-6-14 10:15
没有看出来哪里方便了
select="*//*[@class='topic']/a/text()"  这不是还是xpath

xslt里面是xpath,但是所有的抓取内容用一个xslt,一次性抓下来。

如果手工写这个xslt,十分麻烦,应该用MS谋数台,通过直观标注,只要几分钟就生成一个xslt。参看:http://www.gooseeker.com/doc/thread-1781-1-2.html
举报 使用道具
comboo 新手上路 发表于 2016-6-23 01:12:39 | 显示全部楼层
今天用了,如果自动生成xpath 确实很放方便。
希望教程能够组织的更简练一点。
举报 使用道具
Fuller 管理员 发表于 2016-6-23 09:22:32 | 显示全部楼层
comboo 发表于 2016-6-23 01:12
今天用了,如果自动生成xpath 确实很放方便。
希望教程能够组织的更简练一点。
...

等整个框架都完成了,我们打算重新编辑一下文档,一步步怎样做说清楚
举报 使用道具
您需要登录后才可以回帖 登录 | 立即注册

精彩推荐

  • Gephi社会网络分析-马蜂窝游记文本分词并同
  • Gephi社会网络分析-基于马蜂窝游记文本以词
  • 知乎话题文本根据词语间距筛选后生成共词矩
  • 马蜂窝游记文本分词后以词语间距为筛选条件
  • 学习使用apriori算法挖掘关联关系

热门用户

GMT+8, 2024-3-29 17:37