Python 爬虫网页,解析工具lxml.html(一)

作者: 一墨编程学习 | 来源:发表于2019-04-29 22:11 被阅读2次

    狭义上讲,爬虫只负责抓取,也就是下载网页。而实际上,爬虫还要负责从下载的网页中提取我们想要的数据,即对非结构化的数据(网页)进行解析提取出结构化的数据(有用数据)。比如,我们要抓取了一个新闻页面的网页(html)下来,但我们想要的是这个网页中关于新闻的结构化数据:新闻的标题、新闻的发布时间、新闻的正文等。

    如果你依然在编程的世界里迷茫,不知道自己的未来规划,可以加入我们的Python学习扣qun:784758214,看看前辈们是如何学习的!交流经验!
    自己是一名高级python开发工程师,从基础的python脚本到web开发、爬虫、django、人工智能、数据挖掘等,零基础到项目实战的资料都有整理。
    送给每一位python的小伙伴!分享一些学习的方法和需要注意的小细节,这里是python学习者聚集地

    点击:python技术分享

    所以说,网页下载下来只是第一步,还有重要的一步就是数据提取。不同的爬虫想要的数据不一样,提取的数据也就不一样,但提取方法都是类似的。

    最简单的提取数据的方法,就是使用正则表达式,此种方法简单,提取的逻辑也不能复杂,不然写出的正则表达式就晦涩难懂,甚至不能提取复杂的数据结构。

    经过多年的使用经验,选择了lxml和xpath来解析网页提取结构化数据。顺便说一下 BeautifulSoup,它也是一个很棒的解析HTML的工具,可以使用多个解析器,比如Python标准库的parser,但是速度比较慢,也可以使用lxml作为解析器,但是它的使用方法、API跟lxml不太一样。使用下来,还是lxml的API更舒服。

    lxml 对C语言库 libxml2和 libxslt进行绑定,提供了Pythonic的API,它有一些主要特点:

    • 支持标准的XML
    • 支持(损坏)的HTML
    • 非常快的解析速度
    • Pythonic的API更易于使用
    • 使用Python的unicode字符串
    • 内存安全(没有段错误)
    • 不需要手动管理内存

    总结为一句话就是,C语言的速度和Python的简易相结合的神器。

    lxml有两大部分,分别支持XML和HTML的解析:

    • lxml.etree 解析XML
    • lxml.html 解析html

    lxml.etree可以用来解析RSS feed,它就是一个XML格式的文档。然而爬虫抓取的绝大部分都是html网页,所以,我们这里主要讲述lxml.html解析网页的方法。

    lxml.html 从html字符串生成文档树结构

    我们下载得到的网页就是一串html字符串,如何把它输入给lxml.html模块,从而生成html文档的树结构呢?
    该模块提供了几种不同的方法:

    • parse(filename_url_or_file):
      输入的是一个文件名、URL或文件对象(有read()方法)。
    • document_fromstring(string):
      输入的是一个html的字符串,创建一个HTML文档树结构,它的根节点就是, 和 子节点。
    • fragment_fromstring(string, create_parent=False):
      返回输入字符串的HTML片段。这个片段壁纸只含有一个element(元素),也就是单一节点,除非给出了create_parent 参数,否则会报错。
    • fragments_fromstring(string):
      返回包含输入字符串中所有片段的列表。
    • fromstring(string):
      返回值依据输入字符串而定,如果输入看起来像是一个文档,则返回document_fromstring(string),如果是一个单一片段,则返回fragment_fromstring(string)

    下面我们通过具体示例来说明上面几个方法的不同。

    document_fromstring 的使用方法

    In [1]: import lxml.html  as lh
    
    In [2]: z = lh.document_fromstring('<span>abc</span><span>xyz</span>')
    # 可以看到,它自动加了根节点<html>
    In [3]: z
    Out[3]: <Element html at 0x7fc410667b88>
    
    In [4]: z.tag
    Out[4]: 'html'
    # 还加了<body>节点
    In [5]: z.getchildren()
    Out[5]: [<Element body at 0x7fc4101a3ae8>]
    # 把字符串的两个节点放在了<body>里面
    In [6]: z.getchildren()[0].getchildren()
    Out[6]: [<Element span at 0x7fc410092bd8>, <Element span at 0x7fc410667c28>]
    
    

    fragment_fromstring 的使用

    In [11]: z = lh.fragment_fromstring(‘<div>abc</div><div>xyz</div>’)
    ---------------------------------------------------------------------------
    ParserError                               Traceback (most recent call last)
    <ipython-input-11-a11f9a0f71d1> in <module>()
    ----> 1 z = lh.fragment_fromstring(‘<div>abc</div><div>xyz</div>’)
    
    ~/.virtualenvs/py3.6/lib/python3.6/site-packages/lxml/html/__init__.py in fragment_fromstring(html, create_parent, base_url, parser, **kw)
        850         raise etree.ParserError(
        851             “Multiple elements found (%s)”
    --> 852             % ‘, ‘.join([_element_name(e) for e in elements]))
        853     el = elements[0]
        854     if el.tail and el.tail.strip():
    ParserError: Multiple elements found (div, div)
    # 可以看到,输入是两个节点(element)时就会报错
    # 如果加上 create_parent 参数,就没问题了
    In [12]: z = lh.fragment_fromstring('<div>abc</div><div>xyz</div>', create_parent='p')
    
    In [13]: z.tag
    Out[13]: 'p'
    
    In [14]: z.getchildren()
    Out[14]: [<Element div at 0x7fc40a41a818>, <Element div at 0x7fc40a41aea8>]
    
    

    fragments_fromstring 的使用

    # 输入字符串含有一个节点,则返回包含这一个节点的列表
    In [17]: lh.fragments_fromstring('<div>abc</div>')
    Out[17]: [<Element div at 0x7fc40a124ea8>]
    
    # 输入字符串含有多个节点,则返回包含这多个节点的列表
    In [18]: lh.fragments_fromstring('<div>abc</div><div>xyz</div>')
    Out[18]: [<Element div at 0x7fc40a124b88>, <Element div at 0x7fc40a124f98>]
    
    

    fromstring 的使用

    In [27]: z = lh.fromstring('<div>abc</div><div>xyz</div>')
    
    In [28]: z
    Out[28]: <Element div at 0x7fc40a0eb368>
    
    In [29]: z.getchildren()
    Out[29]: [<Element div at 0x7fc410135548>, <Element div at 0x7fc40a0eb2c8>]
    
    In [30]: type(z)
    Out[30]: lxml.html.HtmlElement
    
    

    这里,fromstring输入的如果是多个节点,它会给加一个父节点并返回。但是像html网页都是从节点开始的,我们使用fromstring() 和 document_fromstring() 都可以得到完整的网页结构。

    从上面代码中我们可以看到,那几个函数返回的都是HtmlElement对象,也就是说,我们已经学会了如何从html字符串得到HtmlElement的对象,下一节我们将学习如何操作HtmlElement对象,从中提取我们感兴趣的数据。

    相关文章

      网友评论

        本文标题:Python 爬虫网页,解析工具lxml.html(一)

        本文链接:https://www.haomeiwen.com/subject/cbzunqtx.html