Python网页解析

续上篇文章,网页抓取到手之后就是解析网页了。

在Python中解析网页的库不少,我最开始使用的是BeautifulSoup,貌似这个也是Python中最知名的HTML解析库。它主要的特点就是容错性很好,能很好地处理实际生活中各种乱七八糟的网页,而且它的API也相当灵活而且丰富。

但是我在自己的正文提取项目中,逐渐无法忍受BeautifulSoup了,主要是因为下面几个原因:

  • 由于BeautifulSoup 3(当前的版本)依赖于Python内建的sgmllib.py,而sgmllib.py有好些无法容忍的问题,因此在解析某些网页的时候无法得到正确的结果
  • 由于BeautifulSoup 3的上层和底层解析完全都是Python的,因此速度简直是无法容忍,有时候简直比网络还要慢

下面我们来一一说说上述的问题。

首先是解析上的问题,看看下面的Python代码:

from BeautifulSoup import BeautifulSoup

html = u‘<div class=my-css>hello</div>‘
print BeautifulSoup(html).find(‘div‘)[‘class‘]
html = u‘<div class=我的CSS类>hello</div>‘
print BeautifulSoup(html).find(‘div‘)[‘class‘]
html = u‘<div class="我的CSS类">hello</div>‘
print BeautifulSoup(html).find(‘div‘)[‘class‘]

第一次print的结果my-css,第二次是啥都没有,第三次print的结果是我们期待的”我的CSS类”。

原来,sgmllib在解析属性的时候使用的正则表达式没有考虑到非ASCII字符的问题。这个问题相对好解决一些,只要我们在程序的开头导入sgmllib模块,然后修改它对应的属性正则表达式变量就可以了,就像下面这样:

import re, sgmllib

sgmllib.attrfind = re.compile(r‘\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*(\‘[^\‘]*\‘|"[^"]*"|[^\s^\‘^\"^>]*))?‘)

第二个问题就稍微麻烦一些了,还是看代码:

from BeautifulSoup import BeautifulSoup

html = u‘<a onclick="if(x>10) alert(x);" href="javascript:void(0)">hello</a>‘
print BeautifulSoup(html).find(‘a‘).attrs

打印的结果是[(u‘onclick‘, u‘if(x>10) alert(x);‘)]

显然,a元素的href属性被弄丢了。原因就是sgmllib库在解析属性的时候一旦遇到了>等特殊符号就会结束属性的解析。要解决这个问题,只能修改sgmllib中SGMLParser的parse_starttag方法,找到292行,即k = match.end(0)这一行,添加下面的代码即可:

if k > j:
    match = endbracket.search(rawdata, k+1)
    if not match: return -1
    j = match.start(0)

至于BeautifulSoup速度慢的问题,那就无法通过简单的代码修改搞定了,只能换一个HTML解析库。实际上,我现在使用的是lxml,它是基于C语言开发的libxml2libxslt库的。经我个人测试,速度比BeautifulSoup 3平均要快10倍。

使用lxml库解析HTML非常简单,兼容性也非常高,大部分实际网站上的网页都可以正确解析。而且lxml使用非常方便的XPath语法进行元素查询,它支持string-length、count等XPath 1.0函数(参见XPath and XSLT with lxml)。不过2.0的函数,如序列操作的函数就不行了,这需要底层libxml2和libxslt库的开发者升级这两个库,添加对XPath 2.0函数的支持才行。

假设我们得到了unicode类型的网页,想要获取所有带href属性的链接,代码如下:

import lxml.html

dom = lxml.html.fromstring(html)
all_links = dom.xpath(‘.//a[@href]‘)

使用lxml也有一些注意事项:

  1. 不能向lxml.html.fromstring传递长度为0的字符串,不然会抛出解析异常。需要事先判断一下,如果长度为零,可以传入<html></html>作为内容
  2. 某些网页不知什么原因,网页内容里有\x00,也就是ASCII编码为0的字符。由于lxml底层是C语言开发的,\x00在C语言里表示字符串结尾,因此需要将这些字符替换一下(html.replace(‘\x00‘, ‘‘))
  3. 一般情况下,为了减少编码猜测的错误,我们传递给lxml.html.fromstring的网页字符串都是unicode字符串,也就是经过编码检测和decode后的字符串。但是如果网页是<?xml开头,而且有编码设定的(如<?xml version="1.0" encoding="UTF-8" ?>这样)的,也就是说是一个XML包裹的HTML,则我们必须将原始字符串传递给lxml,不然lxml也会报异常,因为针对这种文档,lxml会尝试使用自己的解码机制来做
  4. lxml兼容性是有限的,没有主流浏览器那么宽容。因此,有少部分浏览器能大致正常显示的网页,lxml仍然无法解析出来

上面几个问题中,第一二个问题好解决,但是第三个问题最麻烦。因为你事先是不知道这个网页是否是带编码的XML文档的,只有出了ValueError异常才行,但是lxml使用ValueError报告一切错误,因此为了精确一点,你要分析异常的字符串信息才知道是否是这个问题导致的解析异常,如果是的话,则将未解码的网页内容再给lxml传一次,如果不是就报错。

第四个问题很烦人,但是说句实话,我们也做不了太多工作,lxml的兼容性已经相当好了,可行的方法是放弃掉这些网页。要么就换一个工具,不使用lxml做网页解析,但是在Python库里很难找到比lxml更好的HTML解析库。

用lxml解析HTML代码的示例,可以参考正文提取程序里的__create_dom方法。

另外还有一个悬疑问题,那就是在解析网页多,内存压力大的情况下,lxml似乎会出现内存溢出的问题。

我有一个程序,每天都要扫描上万个网页,解析四五千个网页。大概每过一个月或一个半月,这个程序就会导致服务器内存全满,不管是多少内存,全部吃光。我以前一直以为可能是底层代码的重入性问题(因为我用多线程来做的程序),但是后来换成多进程和单线程模式都会出现这个问题,才跟踪到出错的代码就是在调用lxml.html.fromstring的时候溢出的。

不过这个bug超级难以重现,而且我有很多程序都会不停调用lxml(有的每天,有的每小时,有的每次会解析几百个网页)来做HTML解析的工作,可是只有这个程序会偶尔出现溢出情况,太郁闷了。

网上也有类似的报告,但是迄今仍然无法有效重现和确定这个bug。因此我只能写一个脚本限制python程序的占用内存数,然后通过这个脚本来调用python程序,像这样:

#!/bin/bash

ulimit -m 1536000 -v 1536000
python my-prog.py

解析网页还有一些其他要做的工作,例如将非标准(网站自定义的)的HTML标签转化为span或者div,这样才能被识别为正文。剩下的就是调试的工作了。

另外,就在本文写作的时候,BeautifulSoup 4将要发布了,它承诺可以支持Python内建库、lxml与html5lib等不同的HTML解析引擎来进行网页解析,也许新版本的BeautifulSoup也是一个不错的选择。

时间: 2024-10-11 22:06:02

Python网页解析的相关文章

转:Python网页解析:BeautifulSoup vs lxml.html

转自:http://www.cnblogs.com/rzhang/archive/2011/12/29/python-html-parsing.html Python里常用的网页解析库有BeautifulSoup和lxml.html,其中前者可能更知名一点吧,熊猫开始也是使用的BeautifulSoup,但是发现它实在有几个问题绕不过去,因此最后采用的还是lxml:  1. BeautifulSoup太慢.熊猫原来写的程序是需要提取不定网页里的正文,因此需要对网页进行很多DOM解析工作,经过测试

网页解析器

1.网页解析器:从网页中提取有价值的数据. 2.python网页解析的方式: 正则表达式.html.parser(python自带).Beautiful Soup(第三方).lxml(python自带). Beautiful Soup可以使用html.parser或者lxml作为解析器 3.网页解析器就是结构化解析-DOM(Document Object Model)树 4.安装Beautiful Soup以及官网地址 pip install beautifulsoup4 http://www.

Python爬虫解析网页的4种方式 值得收藏

用Python写爬虫工具在现在是一种司空见惯的事情,每个人都希望能够写一段程序去互联网上扒一点资料下来,用于数据分析或者干点别的事情. ? 我们知道,爬虫的原理无非是把目标网址的内容下载下来存储到内存中,这个时候它的内容其实是一堆HTML,然后再对这些HTML内容进行解析,按照自己的想法提取出想要的数据,所以今天我们主要来讲四种在Python中解析网页HTML内容的方法,各有千秋,适合在不同的场合下使用. 首先我们随意找到一个网址,这时我脑子里闪过了豆瓣这个网站.嗯,毕竟是用Python构建的网

Python 网页爬虫

一.要解决的问题 需要解决的是根据自定义的关键词自动搜索google学术,解析搜索到的网页,下载所有相应的论文的PDF链接.这里我们采用Python来实现, 二.Python入门 python 自动缩进:shift+table整块向左缩进,table向右缩进,在修改整块代码时很有用比如将函数变成单独执行时. 了解python的变量,包,函数定义等 三.网页知识 3.1 浏览网页的过程 打开网页的过程其实就是浏览器作为一个浏览的“客户端”,向服务器端发送了 一次请求,把服务器端的文件“抓”到本地,

Python 文本解析器

Python 文本解析器 一.课程介绍 本课程讲解一个使用 Python 来解析纯文本生成一个 HTML 页面的小程序. 二.相关技术 Python:一种面向对象.解释型计算机程序设计语言,用它可以做 Web 开发.图形处理.文本处理和数学处理等等. HTML:超文本标记语言,主要用来实现网页. 三.项目截图 纯文本文件: Welcome to ShiYanLou ShiYanLou is the first experiment with IT as the core of online ed

一个极其简洁的Python网页抓取程序

paip. 混合编程的实现resin4 (自带Quercus ) 配置 php 环境 #---混合编程的类型 1.代码inline 方式 2.使用库/api  解析方式. #----配置resin 支持php resin4默认自动支持php.. 也能手动配置了.web.xml加php的servlet解析..参考Quercus让你的PHP开心在Servlet容器奔跑 #----配置 php.ini路线 运行t.php,,看见 Configuration File (php.ini) Path =>

关于Python json解析过程遇到的TypeError: expected string or buffer

关于Python json解析过程遇到的问题:(爬取天气json数据所遇到的问题http://tianqi.2345.com/) part.1 url--http://tianqi.2345.com/t/wea_history/js/201708/60061_201708.js 返回的数据如下: 这就尴尬了,直接json.loads是返回错误的. 对比了其他网页返回的--http://www.toutiao.com/search_content/?offset=0&format=json&

关于爬虫中常见的两个网页解析工具的分析 —— lxml / xpath 与 bs4 / BeautifulSoup

读者可能会奇怪我标题怎么理成这个鬼样子,主要是单单写 lxml 与 bs4 这两个 py 模块名可能并不能一下引起大众的注意,一般讲到网页解析技术,提到的关键词更多的是 BeautifulSoup 和 xpath ,而它们各自所在的模块(python 中是叫做模块,但其他平台下更多地是称作库),很少被拿到明面上来谈论.下面我将从效率.复杂度等多个角度来对比 xpath 与 beautifulsoup 的区别. 效率 从效率上来讲,xpath 确实比 BeautifulSoup 高效得多,每次分步

Python_爬虫_BeautifulSoup网页解析库

BeautifulSoup网页解析库 from bs4 import BeautifulSoup 0.BeautifulSoup网页解析库包含 的 几个解析器 Python标准库[主要,系统自带;] 使用方法: BeautifulSoup(markup,"html.parser")[注:markup是html文档] Python的内置标准库 案例: ` from bs4 import BeautifulSoup ` soup = BeautifulSoup.(html,'html.pa