(本文系爬虫初学者胡言乱语,不入方家法眼,为免贻笑大方,如若强行阅读,后果自负!)
起因
最近在学习使用python做网络爬虫,看教程挺简单的,使用 requests 简单几行代码就能够把网页内容爬下来,甚是惬意。刚好遇到领导交代一个任务,把某某网站的某某文件给复制下来,保存归档。心下甚喜,学成归来终有用武之地了,于是心急火燎的把活儿揽下来,并对领导夸口,分分钟搞定!
其实爬网站这种事情,古已有之,想当年Flashget(网际快车)和NetAnts(网络蚂蚁)风行的时候,似曾有段时间也支持过所谓的“离线网站”,一直到后来的WebZip、HTTrack,实际上都不能称为十分好用,最容易出现的问题就是该爬的没爬到,不该爬的爬了一堆,也许是我不会用的缘故吧。总之,我就是想利用所学,自己写一个工具。
有了历史经验,咱们要做的第一件事就是,声明一个路径列表,凡是从网页中解析出来的URL,又包含在该路径列表中的,一律往下爬,不要跟我扯什么最大层级,不care,因为太死板,该爬的没爬到,不该爬的爬一堆,就是这货的锅。第二件事情,因为我是用 Beautifulsoup 解析网页并分析页面内包含的URL的,所以得声明一个元素标签匹配规则列表,凡是符合匹配规则的元素,就解析其中包含的 URL放入待爬取URL列表中,这样,遇到不同的网站,只要灵活配置匹配规则即可。
自信满满的以为这样就够了,爬取库(requests)、解析库(Beautifulsoup)、待爬取URL列表、已爬取URL列表、爬取路径控制列表、爬取元素匹配规则列表,还有什么能比这更完美的吗?
事实上,我错了,原本吹嘘为分分钟的事情,最后变成了钟钟钟。。。
来看看我栽了哪些坑吧。。。
1、网页乱码
还能有比网页乱码更常见的坑吗?
我曾经特么天真的以为在使用 requests 时,设置一句 res.encoding = 'utf-8' ,就能得到完美的 utf-8 编码的字符串了呢,事实上我用print 打印出来的网页内容,也的确是正常显示的,可是一旦保存到文件,中文就成了乱码,究竟是什么鬼在捣乱???
费了半天劲,研究了各种字符串转码以及设置写文件时编码格式的办法,总算搞清楚了,原来 python3 默认是 unicode 编码(这里容我插句嘴,实际上我知道 unicode 是一种字符集而不是一种编码格式,甚至 utf-8 只是 unicode 字符集的一种编码格式而已,大家习惯称之为 unicode 编码的编码格式准确的应该称之为 usc2 ,但是谁 care 呢),而保存网页文件时,应该根据网页头部的 charset 而定,例如我们要把 utf-8 格式的网页保存到文件,大概应该要这样子:
f = open(fullname, 'wb') # 用二进制方式打开文件
f.write(content.encode('UTF-8')) # 把要写入的字符串转化为相应的编码
2、网页上看到的元素结构和网页源码里的结构不同
如果我不说出来,这个谜恐怕一辈子无解了,说出来,希望有人能教我。。。
我不知道为什么会是这样子的,我在 chrome里看到的网页元素结构是这样子滴:
可以看到 table 标签下级有个 tbody 标签,可是我把标签匹配规则写成 '.page tbody tr td a' ,死活匹配不到,我反反复复一级一级缩减标签,打印内容,居然惊讶的发现,代码其实是这样子的:
也不知道为何,代码里, table 标签下没有 tbody 标签,直接就到了 tr ,我滴个神,坑爹也不是这种坑法吧,这是什么鬼???
3、css、js等文件都要抓取
一个真正的离线网站工具,并不像我想象中的那样简单,毕竟我们不是在做数据爬虫(除了‘窃取’别人的关键数据外,其他都不 care),我们想要的是一个能够在本地完美呈现的离线网站,所以 css文件、js 文件以及各种图片资源,都要爬取,有意思的是,这些乱七八糟的文件所存放的路径往往千奇百怪,这就意味着要增加我们配置爬取路径控制列表的复杂度,我想,我开始有点理解为什么离线网站工具都用一个简单的层级来控制爬取范围了。。。
忙活了半天,总算还有迹可循,但不排除有些手潮的疯子开发的不可理喻的网站。。。
4、保存图片文件
好吧,再来说说文件保存吧,如果你采用了我在坑1中建议的保存文件的方法,哈哈,恭喜你,你可能已经掉进这个坑了。
如果没有,那么恭喜你!。。。好吧,我还是承认我弄的太久了,已经开始头晕了的事实吧。
实际上保存网页文件时,需要根据字符集来灵活设置,但保存图片资源时,就不要做转码了,用二进制打开文件直接保存 content 即可,例如这样:
import requests
res = requests.get('图片url')
f = open('C:\\web\\photo.jpg', 'wb')
f.write(res.content) # 注意,这里不是 text 了,而是 content
f.close()
哦,对了,顺便说一句,我到现在还没弄清保存 css 和 js 需不需要转码保存,我想如果有中文的话,大概也需要吧,反正我是转了,没出错。
5、不认相对路径
历尽千辛万苦,从一个个坑里爬了上来,却没想,你以为的不一定是你以为的(某东广告语)。。。
我以为什么都爬下来了,相关所需的 css、js、image 一切齐整,当我打开网页时,即可欣赏自己完美的劳动成果了,结果却出人意料的恶心,神马玩意,样式全丢,图片全窟窿。。。心中顿时万马奔腾。。。
用chrome打开调试,看到控制台上一片血红的 Failed to load resource: net::ERR_FILE_NOT_FOUND ,神呐,这是为什么,为什么要如此残酷的对待我。。。
几经检查,找到了原因,原来是 MMP 的 windows 系统不能准确识别链接中的相对路径,比如:
img src="/web/images/next.gif" (原谅我没有加尖括号,不知道为什么,加了尖括号在简书就显示不正常了)
本地网页文件是没办法找到这个文件的,假设我们的 html 文件路径为 C:\offline\project1\web\html\index.html ,那么说明这个 next.gif 则存放在 html 文件的上一级目录(即web目录)下的 images 目录下,所以,这个 url 应该是这样子才能正常工作:
img src="./../../web/images/next.gif"
又或者:
img src="./../images/next.gif"
别小瞧这个修改相对路径。一款离线网站工具够不够好用,主要就靠这里了,这也是我以前用过的离线工具做的唯一值得称道的地方,只可惜我自己在做时,忘得一干二净。好吧,又是一项蛋疼的工作,我就不上代码了。
6、还有更多更深的坑在等你
非常幸运的,领导交代的任务对象只是一个简单的静态网站,而且没有做任何反爬措施,相信我,相比起动态网站,你更不想看到的是反爬的网站。。。至于怎么脱坑,抱歉,我目前还无法自救,更无论救人了。
最后
磕磕巴巴、跌跌撞撞的终于把领导需要的网站爬下来了,而实际上手动保存只需要半个钟头就够了,就在我踌躇满志的把整个离线网站打包发给领导时,领导淡淡的说了一句:“把它们做成一个pdf文件发给我。”,蛋碎。。。又一个悲伤的故事开始了。。。