使用lxml抓取网页

jopen 6年前

现在越来越多的网站提供了API。早前,我们之前已经谈论过XML-RPCREST。即使网站服务正以指数方式增长,但仍然有一些网站提供的信息很松散。特别是政府的网站。如果你对这些网站上的内容着迷,你唯一的选择就是抓取网页。

 

什么是抓取网页?


抓取网页是一种用编程模仿人类浏览网站的技术。为了能在你的程序里能够抓取网页,你需要的工具:

  •   向网站发出HTTP请求
  • 解析HTTP的响应信息并且提取内容

urllib生成HTTP请求,一个标准的Python类模块。一旦你通过网站获得原始的HTML,就需要一种效率的方式去提取内容。

许多程序员谈论到从文本文件中提取信息的时候,很快就会想到用正则表达式。

但是,有一个更好的处理工具。lxml登场,使用类似lxml的工具,你可以把一个HTML文件转换为XML文件。毕竟,一个XHTML文件属于XML文件的一种。就我们知道的网站作者很少关心HTML文件的规范。大部分的网站有不完整的HTML语言。我们必须要处理它们。

但嘿,lxml可以很好的处理。甚至如果你应用在一个不完整的HTML文件,lxml的HTML解析器可以转换成有效的XML文件。但是,在网站抓取时,正则表达式仍然有用。你可以结合着lxml使用正则表达式,特别是当你处理文本节点的时候。

 

在开始之前你应该知道什么?


  •   XML
  • Xpath
  • 一点点的Python  

W3Schools.com 在这些科目上都有很好的教程,可以去XML tutorialXpath tutorial去提高你的知识。

让我们写一个python脚本来练习新学习到的技能。

印度政府有一个列举一些荣誉国会成员的网页。这次练习的目标就是抓取这个网页并且提取荣誉国会成员的名字。

练习的网页地址是:http://164.100.47.132/LssNew/Members/Alphabaticallist.aspx

抛开纷乱,让我们开始编码:

 

import urllib  from lxml import etree  import StringIO

我们可以用urllib模块、lxml、etree抓取网页,并且有了必须得解析对象

   result = urllib.urlopen("http://164.100.47.132/LssNew/Members/Alphabaticallist.aspx")  html = result.read()

在这一点上,我们有了原始的HTML

parser = etree.HTMLParser()  tree   = etree.parse(StringIO.StringIO(html), parser)

我们创建HTML解析器对象,然后把解析器传递给etree。换句话说,我们告诉etree解析先前的HMLT解析对象。我们也可以使用StringIO.StringIO把文件传递给字符串对象\ 

现在,看一下文件的源代码

我们想要的信息在id为” ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1”的表格里

让我们开始构建XPath表达式,为了向里挖掘我们关心部分的数据

 

//table[@id='ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1']

以上的XPath表达式需要id抓取表格节点

“ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1”位于文件

第一行,<tr>,虽然包含表格头部但不是必要的,让我们抓取表格元素中除了第一行所有的行

/table[@id='ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1']/tr[position()>1]

在每个行里,国会成员的名字在第二个单元格<td>中。
过滤XPath表达式,只返回每一行的第二个单元格

//table[@id='ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1']/tr[position()>1]/td[position()=2]

进入我们的目标节点单元,国会成员的名字被包含在<a>标签里
重新定义XPath表达式,抓起文本节点
//table[@id='ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1']/tr[position()>1]/td[position()=2]/a/child::text()

应用XPath表达式在我们的树上,
xpath = "//table[@id='ctl00_ContPlaceHolderMain_Alphabaticallist1_dg1']/tr[position()>1]/td[position()=2]/a/child::text()"  filtered_html = tree.xpath(xpath)

这就是所有我们抓取国会成员名字的代码
filtered_html 变量是一个python列表。列表中的元素国会成员的名字
自己亲自尝试,并查看结果
print filtered_html

实例输出如下:

['Aaroon Rasheed,Shri J.M.', 'Abdul Rahman,Shri ', 'Abdullah,Dr. Farooq', 'Acharia,Shri Basudeb', ...]

当你读到这个文档,如果这个网页已经被移动或者它的内容已改变。请参考attached HTML document.
全部的脚本发表在了gist
欢迎标记和搜索这篇文章
附件          Size


members-of-the-parliment.html  455.22 KB