今天在翻看Evernote的筆記時,看到之前剪輯的一位加國移民文章頗為有趣和實用。于是心血來潮,寫個爬蟲把所有文章都保存下來,哪知道過程中遇到了各種無奈問題,遂寫篇博客以記錄學(xué)習過程。
坑1:bs4的局限性
打算爬的這個新浪博客,原始URL如下:
rootURL = 'http://blog.sina.com.cn/s/articlelist_1750617077_0_1.html'
用瀏覽器查看了一下get下來頁面的html,里面很標準的用<a標簽包含了每篇文章的鏈接。
之前有用BeautifulSoup和Requests的經(jīng)驗,因此很自然的準備用上這兩個工具,利用bs4的解析標簽?zāi)芰?,把所有的uri全部提取出來
from bs4 import BeautifulSoup
import requests
r = requests.get(URL)
html = r.text
bs = BeautifulSoup(html).find_all('a')
for u in bs:
uri = item.get('href')
然而,想象很美好,現(xiàn)實很骨感。拿到的uri是None,也就是說,毛都沒有解析出來。這可有點奇怪了,遂想起bs4的一些局限性,仔細對照了一下博客的html源碼,原來<a全部包含在js里了,難怪bs4毫無作用(坑1:bs4對包含在js里的標簽毫無卵用)。
坑2:中文亂碼
既然bs4對這個博客無法作用,只有采用方案2:正則表達式。
根據(jù)html源碼,寫出正則:
re_blogs = 'target="_blank"\s*href="(.+.html)">(.+)</a></span>'
用正則parser html的函數(shù):
def parseHtml(RE, URL):
RE_STRING = RE
try:
req = requests.get(URL)
html_doc = req.content
info = re.findall(RE_STRING, html_doc)
return info
except Exception as e:
print(e)
return None
這下總可以了吧?結(jié)果,依然沒有拿到所有的uri。一度懷疑是自己的正則寫錯了,可是反復(fù)用在線正則工具進行比對,都沒有問題。此時博主心中已經(jīng)一千頭草泥馬跑過了,設(shè)斷點debug!結(jié)果發(fā)現(xiàn)問題出在了req.content問題上,其賦給html_doc的中文部分是亂碼,所以導(dǎo)致re.findall出錯。查看了百度上的一些解釋,猜測是encode格式的問題,加了如下一個debug flag:
req = requests.get(URL)
print(req.encoding)
html_doc = req.content
果然,print出來的是一種ISO-8859-1的格式,時間關(guān)系也沒深究了,直接轉(zhuǎn)utf-8搞起
def parseHtml(RE, URL):
RE_STRING = RE
try:
req = requests.get(URL)
html_doc = str(req.content,'utf-8')#就是這里改動了一點點
info = re.findall(RE_STRING, html_doc)
return info
except Exception as e:
print(e)
return None
此時終于把所有的uri取出來了!
坑3:file.write()亂碼##
繼續(xù)完善代碼,主要考慮到翻頁如何實現(xiàn),繼續(xù)用萬能的正則:
設(shè)計提取下一頁的正則表達式;
re_next_page = '<a\s*(href=".+.html)"\s*title=".+">\d</a>'
由于新浪博客實在是格式不規(guī)整,html源碼里缺少換行符,原本應(yīng)該正常解析成3個的翻頁url,愣是被搞成了一個連在一起的四不像,如下這樣:
title="跳轉(zhuǎn)至第 2 頁">2</a></li><li><a title="跳轉(zhuǎn)至第 3 頁">3</a></li><li><a href="http://blog.sina.com.cn/s/articlelist_1750617077_0_4.html
只好繼續(xù)寫一個把uri分離的正則:
re_string = 'href="(.*?)"'
把所有頁鏈接,存到一個list中:
all_urls = parseHtml(re_next_page,rootURL)
pages = re.findall(re_string,all_urls[0])#pages is a list
pages.append(rootURL)
接著是從url中保存html的函數(shù):
def getHtmlFromLink(link,name):
for i,url in enumerate(link):
req = requests.get(url)
html_doc = str(req.content, 'utf-8')#子鏈接的內(nèi)容
html_name = name[i]
file_path = 'D://blogs//'+html_name+'.html'
f = open(file_path,'w')
f.write(html_doc)
f.close()
最后是main部分
link = []
name = []
for url in pages:
blogs = parseHtml(re_blogs, url)
for uri in blogs:
link.append(uri[0])
name.append(uri[1])
getHtmlFromLink(link,name)
高潮來了,看著爬蟲開始工作并保存,心里爽爽爽!??!然后用瀏覽器打開保存下來的一個html,他媽的亂碼又來了?。?!
這里筆者就不是很能理解了,已經(jīng)把要寫入file的string都用utf-8編碼好了,為啥中文還是亂碼了?!
在百度上苦尋答案依然無所獲,和一個朋友交流了一下,試著修改了一下write方法參數(shù)
f = open(file_path,'w',-1,'utf-8')
終于正常了,媽蛋!~!

至此,完成了一個博客所有文章的爬取過程,深刻描述了三個大坑以及筆者的心路歷程。
細心的你們一定發(fā)現(xiàn)了,圖片好像還沒保存到本地呢。這個準備放到下一次的文章中咯~