1. 程式人生 > >python——爬蟲&問題解決&思考(四)

python——爬蟲&問題解決&思考(四)

參數 多層 得到 簡單 odi 用兩個 src http 輸出

  繼續上一篇文章的內容,上一篇文章中已經將url管理器和下載器寫好了。接下來就是url解析器,總的來說這個模塊是幾個模塊中比較難的。因為通過下載器下載完頁面之後,我們雖然得到了頁面,但是這並不是我們想要的結果。而且由於頁面的代碼很多,我們很難去裏面找到自己想要的數據。所幸,我們下載的是html頁面,它是一種由多個多層次的節點組成的樹型結構的文本文件。所以,相較於txt文件,我們更加容易定位到我們要找的數據塊。現在我們要做的就是去原頁面去分析一下,我們想要的數據到底在哪。

  打開百度百科pyton詞條的頁面,然後按F12調出開發者工具。通過使用工具,我們就能定位到頁面的內容:

技術分享

技術分享

  這樣我們就找到了我們想要的信息處在哪個標簽裏了。

 1 import bs4
 2 import re
 3 from urllib.parse import urljoin
 4 class HtmlParser(object):
 5     """docstring for HtmlParser"""
 6     def _get_new_urls(self, url, soup):
 7         new_urls = set()
 8         links = soup.find_all(a, href = re.compile(r/item/.))
 9         for link in links:
10 new_url = re.sub(r(/item/)(.*), r\1%s % link.getText(), link[href]) 11 new_full_url = urljoin(url, new_url) 12 new_urls.add(new_full_url) 13 return new_urls 14 15 def _get_new_data(self, url, soup): 16 res_data = {} 17 #url 18 res_data[
url] = url 19 #<dd class="lemmaWgt-lemmaTitle-title"> 20 title_node = soup.find(dd, class_ = "lemmaWgt-lemmaTitle-title").find(h1) 21 res_data[title] = title_node.getText() 22 #<div class="lemma-summary" label-module="lemmaSummary"> 23 summary_node = soup.find(div, class_ = "lemma-summary") 24 res_data[summary] = summary_node.getText() 25 return res_data 26 27 def parse(self, url, html_cont): 28 if url is None or html_cont is None: 29 return 30 soup = bs4.BeautifulSoup(html_cont, lxml) 31 new_urls = self._get_new_urls(url, soup) 32 new_data = self._get_new_data(url, soup) 33 return new_urls, new_data

  解析器只有一個外部方法就是parse方法,

    a.首先它會接受url, html_cont兩個參數,然後進行判斷頁面內容是否為空

    b.調用bs4模塊的方法來解析網頁內容,‘lxml‘為文檔解析器,默認的為html.parser,bs官方推薦我們用lxml,那就聽它的吧,誰讓人家是官方呢。

    c.接下來就是調用兩個內部函數來獲取新的url列表和數據

    d.最後將url列表和數據返回

  這裏有一些註意點

    1.bs的方法調用還有一個參數,from_encoding 這個和我在下載器那裏的重復了,所以我就取消了,兩個的功能是一樣的。

    2.獲取url列表的內部方法,需要用到正則表達式,這裏我也是摸著石頭過河,不是很會,中間也調試過許多次。

    3.數據是放在字典中的,這樣可以通過key來增改刪除數據。

最好,就直接數據輸出了,這個比較簡單,直接上代碼。

  

 1 class HtmlOutputer(object):
 2     """docstring for HtmlOutputer"""
 3     def __init__(self):
 4         self.datas = []
 5     def collect_data(self, new_data):
 6         if new_data is None:
 7             return
 8         self.datas.append(new_data)
 9     def output_html(self):
10         fout = open(output1.html, w, encoding = utf-8)
11         fout.write(<html>)
12         fout.write(<head><meta charset="utf-8"></head>)
13         fout.write(<body>)
14         fout.write(<table>)
15         for data in self.datas:
16             fout.write(<tr>)
17             fout.write(<td>%s</td> % data[url])
18             fout.write(<td>%s</td> % data[title])
19             fout.write(<td>%s</td> % data[summary])
20             fout.write(</tr>)
21         fout.write(</table>)
22         fout.write(</body>)
23         fout.write(</html>)
24         fout.close()

  這裏也有兩個註意點

    1.fout = open(‘output1.html‘, ‘w‘, encoding = ‘utf-8‘),這裏的encoding參數一定要加,不然會報錯,在windows平臺,它默認是使用gbk編碼來寫文件的。

    2.fout.write(‘<head><meta charset="utf-8"></head>‘),這裏的meta標簽也要加上,因為要告訴瀏覽器使用什麽編碼來渲染頁面,這裏我一開始沒加弄了很久,我打開頁面的內容,發現裏面是中文的,結果瀏覽器展示的就是亂碼。總的來說,因為整個頁面采集過程結果好幾個模塊,所以編碼問題要非常小心,不然少不留神就會出錯。

  最後總結,這段程序還有許多方面可以深入探討:

  1.頁面的數據量過小,我嘗試了10000個頁面的爬取。一旦數據量劇增之後,就會帶來一下問題,第一是待爬取url和已爬取url就不能放在set集合中了,要麽放到radi緩存服務器裏,要麽放到mysql數據庫中

  2.第二,數據也是同樣的,字典也滿足不了了,需要專門的數據庫來存放

  3.第三量上去之後,對爬取效率就有要求了,那麽多線程就要加進來

  4.第四,一旦布置好任務,單臺服務器的壓力會過大,而且一旦宕機,風險很大,所以分布式的高可用架構也要跟上來

  5.一方面是頁面的內容過於簡單,都是靜態頁面,不涉及登錄,也不涉及ajax動態獲取

  6.這只是數據采集,後續還有建模,分析…………

綜上所述,路還遠的很呢,加油!

  

python——爬蟲&問題解決&思考(四)