zoukankan      html  css  js  c++  java
  • Python使用xslt提取网页数据

    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格式


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

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

    可以拷贝运行下面的代码(在windows10, python3.2下测试通过):

    from urllib import request
    from lxml import etree
    url="http://www.sina.com/cn/forum/7"  #由于博客园的限制,如果你要运行该段代码的话请将‘sina’替换成‘gooseeker’
    conn=request.urlopen(url)
    
    doc = etree.HTML(conn.read())
    
    xslt_root = etree.XML("""
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:template match="/">
    <列表>
    <xsl:apply-templates select="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表"/>
    </列表>
    </xsl:template>
    
    
    
    <xsl:template match="table/tbody/tr[position()>=1]" mode="list">
    <item>
    <标题>
    <xsl:value-of select="*//*[@class='topic']/a/text()"/>
    <xsl:value-of select="*[@class='topic']/a/text()"/>
    <xsl:if test="@class='topic'">
    <xsl:value-of select="a/text()"/>
    </xsl:if>
    </标题>
    <回复数>
    <xsl:value-of select="*//*[@class='replies']/text()"/>
    <xsl:value-of select="*[@class='replies']/text()"/>
    <xsl:if test="@class='replies'">
    <xsl:value-of select="text()"/>
    </xsl:if>
    </回复数>
    </item>
    </xsl:template>
    
    <xsl:template match="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表">
    <item>
    <list>
    <xsl:apply-templates select="table/tbody/tr[position()>=1]" mode="list"/>
    </list>
    </item>
    </xsl:template>
    </xsl:stylesheet>""")
    
    transform = etree.XSLT(xslt_root)
    result_tree = transform(doc)
    print(result_tree)


    源代码请通过本文结尾的GitHub源下载。

    2.3,抓取结果

    得到的抓取结果如下图:


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

    我们对2.2的代码再做进一步修改,增加翻页抓取和存结果文件功能,代码如下:

    from urllib import request
    from lxml import etree
    import time
    
    xslt_root = etree.XML("""
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:template match="/">
    <列表>
    <xsl:apply-templates select="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表"/>
    </列表>
    </xsl:template>
    
    
    
    <xsl:template match="table/tbody/tr[position()>=1]" mode="list">
    <item>
    <标题>
    <xsl:value-of select="*//*[@class='topic']/a/text()"/>
    <xsl:value-of select="*[@class='topic']/a/text()"/>
    <xsl:if test="@class='topic'">
    <xsl:value-of select="a/text()"/>
    </xsl:if>
    </标题>
    <回复数>
    <xsl:value-of select="*//*[@class='replies']/text()"/>
    <xsl:value-of select="*[@class='replies']/text()"/>
    <xsl:if test="@class='replies'">
    <xsl:value-of select="text()"/>
    </xsl:if>
    </回复数>
    </item>
    </xsl:template>
    
    <xsl:template match="//*[@id='forum' and count(./table/tbody/tr[position()>=1 and count(.//*[@class='topic']/a/text())>0])>0]" mode="列表">
    <item>
    <list>
    <xsl:apply-templates select="table/tbody/tr[position()>=1]" mode="list"/>
    </list>
    </item>
    </xsl:template>
    </xsl:stylesheet>""")
    
    baseurl="http://www.sina.com/cn/forum/7"   #由于博客园的限制,如果你要运行该代码的话,将'sina'替换成'gooseeker‘
    basefilebegin="jsk_bbs_"
    basefileend=".xml"
    count=1
    while (count < 12):
            url=baseurl + "?page=" + str(count)
            conn=request.urlopen(url)
            doc = etree.HTML(conn.read())
            transform = etree.XSLT(xslt_root)
            result_tree = transform(doc)
            print(str(result_tree))
            file_obj=open(basefilebegin+str(count)+basefileend,'w',encoding='UTF-8')
            file_obj.write(str(result_tree))
            file_obj.close()
            count+=1
            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,增加最后一章源代码下载源

  • 相关阅读:
    mybatis:mybatis再总结
    shiro:RememberMe
    shiro:session管理
    shiro:缓存管理
    shiro:授权管理
    shiro:密码加密(加密、加盐加密)
    spring:spring再总结(ioc、aop、DI等)
    SpringBoot:整合layui、退出功能
    layui:内置模块(日期与时间、数据表格)
    nuxtjs中配置配置env
  • 原文地址:https://www.cnblogs.com/gooseeker/p/5501716.html
Copyright © 2011-2022 走看看