每一个可以努力的日子,都是一份厚礼。
Python写爬虫——抓取网页并解析HTML
CUHK 上学期有门课叫做 Semantic Web,课程 project 是要搜集整个系里面的教授信息,输入到一个系统里,能够完成诸如“如果选了A教授的课,因时间冲突,B教授的哪些课不能选”、“和A教授实验室相邻的实验室都是哪些教授的”这一类的查询。这就是所谓的“语义网”了啊。。。然而最坑爹的是,所有这些信息,老师并没有给一个文档或者数据库,全要靠自己去系主页上搜集。唯一的想法是写个爬虫,令人悲哀的是,所有做这个 project 的同学,都是纯人肉手工完成,看得我只想扶墙。。。
从网页中抓取特定信息,我觉得这是一个普遍性的问题,以后经常会遇到。幸亏那个 project 只是需要我们系的所有教授的信息,大家人工也就算了。如果需要抓取的信息是海量的,举个栗子,把淘宝上所有的商品目录抓下来,那岂不是要吐血而亡?我决定好好把爬虫研究一下。
之前波波写过一个 java 程序,利用 HTML Parser 去解析团购网站 meituan.com 然后把每天的团购信息存到数据库里。稍微改改再爬爬拉手糯米,做个前端,一个团购导航站就问世了。我把程序跑了一下,全自动搜集,不算太复杂。
但是,我觉得 java 太啰嗦,不够简洁。Python 这个脚本语言开发起来速度很快,一个活生生的例子是因有关政策 verycd 开始自我阉割,有网友为了抢救资源,把整个 verycd 站爬了下来,镜像为 SimpleCD.org。看了一下爬虫 源代码,其实挺简单。使用方法:
python sitecopy.py http://www.163.com |
看看效果:http://www.lovelucy.info/demo/www.163.com
1. 获取html页面
其实,最基本的抓站,两句话就可以了
import urllib2 content = urllib2.urlopen('http://XXXX').read() |
这样可以得到整个 html 文档,关键的问题是我们可能需要从这个文档中获取我们需要的有用信息,而不是整个文档。这就需要解析充满了各种标签的 html。
2. 解析 html
SGMLParser
Python 默认自带 HTMLParser 以及 SGMLParser 等等解析器,前者实在是太难用了,我就用 SGMLParser 写了一个示例程序:
import urllib2 from sgmllib import SGMLParser class ListName(SGMLParser): def __init__(self): SGMLParser.__init__(self) self.is_h4 = "" self.name = [] def start_h4(self, attrs): self.is_h4 = 1 def end_h4(self): self.is_h4 = "" def handle_data(self, text): if self.is_h4 == 1: self.name.append(text) content = urllib2.urlopen('http://list.taobao.com/browse/cat-0.htm').read() listname = ListName() listname.feed(content) for item in listname.name: print item.decode('gbk').encode('utf8') |
很简单,这里定义了一个叫做 ListName
的类,继承 SGMLParser
里面的方法。使用一个变量 is_h4
做标记判定 html 文件中的 h4
标签,如果遇到 h4
标签,则将标签内的内容加入到 List
变量 name
中。解释一下 start_h4()
和 end_h4()
函数,他们原型是 SGMLParser 中的
start_tagname(self, attrs)
end_tagname(self)
tagname
就是标签名称,比如当遇到 <pre>
,就会调用 start_pre
,遇到 </pre>
,就会调用 end_pre
。attrs
为标签的参数,以 [(attribute, value), (attribute, value), ...]
的形式传回。
输出:
虚拟票务 数码市场 家电市场 女装市场 男装市场 童装童鞋 女鞋市场 男鞋市场 内衣市场 箱包市场 服饰配件 珠宝饰品 美容市场 母婴市场 家居市场 日用市场 食品/保健 运动鞋服 运动户外 汽车用品 玩具市场 文化用品市场 爱好市场 生活服务
如果有乱码,可能是与网页编码不一致,需要替换最后一句 deconde()
的参数,我在香港淘宝默认用的是繁体编码。各位可以 copy 上面的代码自己试试,把淘宝的商品目录抓下来,就是这么简单。稍微改改,就可以抽取二级分类等其他信息。
pyQuery
pyQuery 是 jQuery 在 python 中的实现,能够以 jQuery 的语法来操作解析 HTML 文档,十分方便。使用前需要安装,easy_install pyquery 即可,或者 Ubuntu 下
sudo apt-get install python-pyquery |
以下例子:
from pyquery import PyQuery as pyq doc=pyq(url=r'http://list.taobao.com/browse/cat-0.htm') cts=doc('.market-cat') for i in cts: print '====',pyq(i).find('h4').text() ,'====' for j in pyq(i).find('.sub'): print pyq(j).text() , print '\n' |
BeautifulSoup
有个头痛的问题是,大部分的网页都没有完全遵照标准来写,各种莫名其妙的错误令人想要找出那个写网页的人痛打一顿。为了解决这个问题,我们可以选择著名的 BeautifulSoup 来解析 html 文档,它具有很好的容错能力。
还是用例子来说明吧。我在工作时遇到一个需求,是要查询某一个 NS 服务器的所有域名,通过搜索我找到 sitedossier.com 这个网站可以提供域名服务器的信息。那么就要写个爬虫来抓查询结果了,看上去查询结果页面就是一个 ol 列表,并不复杂。
有点棘手的是如果结果太多,它会进行分页。爬虫需要能够(1)自动翻页(2)知道最后一页。最后一页有“End of list”字符串,所以可以通过正则表达式搞定。
代码开源在 Github 上,几个函数都还算清晰可读,这里就不贴了。感觉这段脚本还是有点小用处的,比如你可以查到迄今为止有 304373 个域名在用 DNSPOD 解析,或者你想看看新浪 SAE 上到底有多少个网站,又或者了解下百度有些什么域名,观察域名规律抢注近似域名神马的……
用法:
$ python crawler_ns.py -ns dns.baidu.com # 保存结果到文件: $ python crawler_ns.py -ns dns.baidu.com >> result.txt |
BeautifulSoup 功能强大,我还在研究学习。有进展会更新本文。
这篇文章由lovelucy于2011-06-10 22:33发表在编程。你可以订阅RSS 2.0 也可以发表评论或引用到你的网站。除特殊说明外文章均为本人原创,并遵从署名-非商业性使用-相同方式共享创作协议,转载或使用请注明作者和来源,尊重知识分享。 |
批评不自由
则赞美无意义
Google Chrome 69.0.3497.81 Windows NT 大约4年前
Traceback (most recent call last):
File “C:/Users/Administrator/Desktop/爬虫.py”, line 1, in
import urllib2
ModuleNotFoundError: No module named ‘urllib2’
这什么意思
Google Chrome 50.0.2661.102 Ubuntu Linux 大约8年前
你好,教一个问题
我想通过Shadowsocks翻墙
代码为:sslocal -s tky.jp.v0.ss-fast.com -p 873 -k f6YVx3 -b 127.0.0.1 -l 1080
但Shadowsocks密码15分钟变换一次
密码网页https://www.ss-fast.com/ucenter/#?act=free_plan
用python怎样抓取密码并实现15分钟更新执行上述代码,“f6YVx3”为密码15分钟变换一次
Google Chrome 42.0.2311.135 Mac OS X 10_10_3 大约9年前
😛 非常感谢!找了好久,终于发现有一篇实用的教程了~
Google Chrome 39.0.2171.95 Mac OS X 10_10_1 大约9年前
正好是我需要的嘿嘿 😀
Google Chrome 28.0.1500.71 Ubuntu Linux 大约10年前
很受用。
博客不错,订阅了。
Mozilla Firefox 20.0 Windows 7 大约11年前
帮你指出个问题,你没发现你采集的图片都是模糊的吗? 问题出再这个函数”urllib2.urlopen(‘http://XXXX’).read()“ 这个函数采集图片的时候应该用二进制方式打开文件,然后写入。 还有一个简单的方法就是 urllib.urlretrieve(img_url, save_name) 直接保存成文件
Google Chrome 27.0.1453.110 Windows 7 大约11年前
Thanks. 我当时也是初学
Google Chrome 31.0.1650.48 Windows 7 大约10年前
楼主 确实非常用功了, 在提取特定的资源时 python 的 re 库 也是不错的选择,正则很强大
Mozilla Firefox 18.0 Windows 7 大约11年前
有个request模块, 相当好用! 😛
Google Chrome 24.0.1312.2 Mac OS X Lion 10_7_4 大约11年前
😛
Internet Explorer 8.0 Windows XP 大约11年前
好文~!我等新手需要的就是这种一点点积累起来的经验分享啊 🙄 🙄
不过我感觉把这个称为semantic web有点…… 😕
这个查询系统唯一需要理解和计算的是教授们的课程安排,这些应该都是用特定格式整理好的,虽然也是自然语言,但是几乎可以当成是个纯数学问题了。CUHK没有计算语言学这个研究方向吧?
看了楼上的讨论,同问如何解析js……楼主试过wget一定也分享一下经验啊。还有,通过浏览器的方式解析js应该是很靠谱的。python先用正则或者Parser得到参数,调用webkit,传入参数,抓取内容,是这样吗?
Google Chrome 20.0.1132.57 Windows 7 大约11年前
最近也在折腾类似的玩意儿。
我使用的最蛋疼的C++来做的
我做的是一键仿站工具。
提取用的是正则表达式。
自动识别文件编码,文件转码之类的都搞定了。但是有3个问题没能得到解决
希望能得到指点。
1、对于伪静态的url。我提取到css路径为相对路径。这时候,应该如何还原成绝对路径。
2、对于源码里面的flash提取,遇到了麻烦。因为大部分都是用js输出的,而不是html标签。
3、需要解析js从而获取js输出的内容,比如图片、flash、iframe等.
后来,准备用python作为C++的脚本,协同工作。但是VC+py3.3 不知道如何发布到未安装python的计算机上。
还有个想法就是,调用jquery来提取各种资源。
希望能给点意见,谢谢! 🙄
Google Chrome 23.0.1271.64 Windows 7 大约11年前
你搞得太复杂了
js脚本输出的内容很难直接获取,你需要一个javascript执行引擎,比如webkit
试试 http://www.phantomjs.org/
以浏览器的方式处理页面,然后输出
Google Chrome 20.0.1132.57 Windows 7 大约11年前
webkit和V8都是比较不错的选择。但是这样工作量会增加很多,文件体积也显得很大。不过,还是值得考虑
Google Chrome 23.0.1271.64 Windows 7 大约11年前
By the way
其实,wget很强大……
Google Chrome 20.0.1132.57 Windows 7 大约11年前
wget能解析js吗?
下载部分,我确实准备用wget来做的 😈
Google Chrome 23.0.1271.64 Windows XP 大约11年前
向你学习!!! ❓
Google Chrome 22.0.1229.79 Linux 大约11年前
菜鸟开始学python,谢谢你的这篇文章
Google Chrome 22.0.1229.94 Windows 7 大约11年前
我也是python菜鸟,多多交流 ^_^
Google Chrome 15.0.874.121 Linux 大约12年前
😎 确实 用正则是王道啊~~
Google Chrome 19.0.1084.56 Windows 7 大约12年前
你这个用正则表达式写好一点