Python爬蟲與數據分析之爬蟲技能:urlib庫、xpath選擇器、正則表達式

專欄目錄:html

Python爬蟲與數據分析之python教學視頻、python源碼分享,pythonnode

Python爬蟲與數據分析之基礎教程:Python的語法、字典、元組、列表python

Python爬蟲與數據分析之進階教程:文件操做、lambda表達式、遞歸、yield生成器web

Python爬蟲與數據分析之模塊:內置模塊、開源模塊、自定義模塊正則表達式

Python爬蟲與數據分析之爬蟲技能:urlib庫、xpath選擇器、正則表達式sql

Python爬蟲與數據分析之京東爬蟲實戰:爬取京東商品並存入sqlite3數據庫chrome

Python爬蟲與數據分析之二手車平臺數據獲取和分析數據庫

Python爬蟲與數據分析之python開源爬蟲項目彙總編程

 

urllib和urllib2json

HTTPHTTPS

HTTP協議(HyperText Transfer Protocol,超文本傳輸協議):是一種發佈和接收 HTML頁面的方法。

HTTPS(Hypertext Transfer Protocol over Secure Socket Layer)簡單講是HTTP的安全版,在HTTP下加入SSL層。

SSL(Secure Sockets Layer 安全套接層)主要用於Web的安全傳輸協議,在傳輸層對網絡鏈接進行加密,保障在Internet上數據傳輸的安全。

瀏覽器發送HTTP請求的過程:

1. 當用戶在瀏覽器的地址欄中輸入一個URL並按回車鍵以後,瀏覽器會向HTTP服務器發送HTTP請求。HTTP請求主要分爲「Get」和「Post」兩種方法。

2. 當咱們在瀏覽器輸入URL http://www.baidu.com 的時候,瀏覽器發送一個Request請求去獲取 http://www.baidu.com 的html文件,服務器把Response文件對象發送回給瀏覽器。

3. 瀏覽器分析Response中的 HTML,發現其中引用了不少其餘文件,好比Images文件,CSS文件,JS文件。 瀏覽器會自動再次發送Request去獲取圖片,CSS文件,或者JS文件。

4. 當全部的文件都下載成功後,網頁會根據HTML語法結構,完整的顯示出來了。

URLUniform / Universal Resource Locator的縮寫)

定義:統一資源定位符,是用於完整地描述Internet上網頁和其餘資源的地址的一種標識方法。

基本格式:scheme://host[:port#]/path/…/[?query-string][#anchor]

  • scheme:協議(例如:http, https, ftp)
  • host:服務器的IP地址或者域名
  • port#:服務器的端口(若是是走協議默認端口,缺省端口80)
  • path:訪問資源的路徑
  • query-string:參數,發送給http服務器的數據
  • anchor:錨(跳轉到網頁的指定錨點位置)

客戶端HTTP請求

URL只是標識資源的位置,而HTTP是用來提交和獲取資源。客戶端發送一個HTTP請求到服務器的請求消息,包括如下格式:

請求行請求頭部空行請求數據

一個典型的HTTP請求

1 GET https://www.baidu.com/ HTTP/1.1
2 Host: www.baidu.com
3 Connection: keep-alive
4 Upgrade-Insecure-Requests: 1
5 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36
6 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
7 Accept-Encoding: gzip, deflate, br
8 Accept-Language: zh,zh-CN;q=0.8,ar;q=0.6,zh-TW;q=0.4
9 Cookie: BAIDUID=AE4D1DA6B2D6689BB8C557B3436893E3:FG=1; BIDUPSID=AE4D1DA6B2D6689BB8C557B3436893E3; PSTM=1501466227; BD_UPN=12314353; BD_CK_SAM=1; PSINO=1; H_PS_PSSID=1420_25548_21080_20929; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDSVRTM=0

 

 

HTTP請求方法

 

序號   方法      描述
1     GET       請求指定的頁面信息,並返回實體主體。
2     HEAD      相似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭
3     POST      向指定資源提交數據進行處理請求(例如提交表單或者上傳文件),數據被包含在請求體中。POST請求可能會致使新的資源的創建和/或已有資源的修改。
4     PUT       從客戶端向服務器傳送的數據取代指定的文檔的內容。
5     DELETE    請求服務器刪除指定的頁面。
6     CONNECT   HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。
7     OPTIONS   容許客戶端查看服務器的性能。
8     TRACE     回顯服務器收到的請求,主要用於測試或診斷。

 

主要方法getpost請求

  • GET是從服務器上獲取數據,POST是向服務器傳送數據
  • GET請求參數顯示,都顯示在瀏覽器網址上,HTTP服務器根據該請求所包含URL中的參數來產生響應內容,即「Get」請求的參數是URL的一部分。 例如: http://www.baidu.com/s?wd=Chinese
  • POST請求參數在請求體當中,消息長度沒有限制並且以隱式的方式進行發送,一般用來向HTTP服務器提交量比較大的數據(好比請求中包含許多參數或者文件上傳操做等),請求的參數包含在「Content-Type」消息頭裏,指明該消息體的媒體類型和編碼.

HTTP響應狀態碼

瀏覽器內核

 

瀏覽器                         內核
IE                           Trident
Chrome                       Webkit
Firefox                      Gecho
Opera                        Pesto
Safari(Apple)                Webkit 

 

HTTP代理工具Fiddler
Fiddler是一款強大Web調試工具,它能記錄全部客戶端和服務器的HTTP請求.

 

Request部分詳解

  1. Headers —— 顯示客戶端發送到服務器的 HTTP 請求的 header,顯示爲一個分級視圖,包含了 Web 客戶端信息、Cookie、傳輸狀態等。
  2. Textview —— 顯示 POST 請求的 body 部分爲文本。
  3. WebForms —— 顯示請求的 GET 參數 和 POST body 內容。
  4. HexView —— 用十六進制數據顯示請求。
  5. Auth —— 顯示響應 header 中的 Proxy-Authorization(代理身份驗證) 和 Authorization(受權) 信息.
  6. Raw —— 將整個請求顯示爲純文本。
  7. JSON - 顯示JSON格式文件。
  8. XML —— 若是請求的 body 是 XML 格式,就是用分級的 XML 樹來顯示它。

 Responser部分詳解

  1. Transformer —— 顯示響應的編碼信息。
  2. Headers —— 用分級視圖顯示響應的 header。
  3. TextView —— 使用文本顯示相應的 body。
  4. ImageVies —— 若是請求是圖片資源,顯示響應的圖片。
  5. HexView —— 用十六進制數據顯示響應。
  6. WebView —— 響應在 Web 瀏覽器中的預覽效果。
  7. Auth —— 顯示響應 header 中的 Proxy-Authorization(代理身份驗證) 和 Authorization(受權) 信息。
  8. Caching —— 顯示此請求的緩存信息。
  9. Privacy —— 顯示此請求的私密 (P3P) 信息。
  10. Raw —— 將整個響應顯示爲純文本。
  11. JSON - 顯示JSON格式文件。
  12. XML —— 若是響應的 body 是 XML 格式,就是用分級的 XML 樹來顯示它 。

 瞭解了這些知識後,接下來真正邁向爬蟲之路.......

urllib2

所謂網頁抓取,就是把URL地址中指定的網絡資源從網絡流中讀取出來,保存到本地。 在Python中有不少庫能夠用來抓取網頁,先學習urllib2

urllib2模塊直接導入就能夠用,在python3中urllib2被改成urllib.request

開始爬蟲須要準備的一些工具

(1)下載Fiddeler抓包工具,百度直接下載安裝就能夠(抓包)

(2)下載chrome瀏覽器代理插件 Proxy-SwitchyOmega(代理)

(3)下載chrome瀏覽器插件XPath(解析HTML)

(4)工具網站:http://www.json.cn/     (json解析網站)

http://tool.chinaz.com/tools/urlencode.aspx     (url編碼解碼網站)

先寫個簡單的爬蟲百度頁面

urlopen

 

1 # _*_ coding:utf-8 _*_
2 import urllib2
3  
4 #向指定的url地址發送請求,並返回服務器響應的類文件對象
5 response = urllib2.urlopen('http://www.baidu.com/')
6 #服務器返回的類文件對象支持python文件對象的操做方法
7 #read()方法就是讀取文件裏的所有內容,返回字符串
8 html = response.read()
9 print html

 

urllib2默認的User-Agent是Python-urllib/2.7,容易被檢查到是爬蟲,因此咱們要構造一個請求對象,要用到request方法。

模擬瀏覽器訪問

瀏覽器訪問時經過抓包工具得到的headers信息以下:

 

 1 GET https://www.baidu.com/ HTTP/1.1
 2 Host: www.baidu.com
 3 Connection: keep-alive
 4 Cache-Control: max-age=0
 5 Upgrade-Insecure-Requests: 1
 6 User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36
 7 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
 8 Accept-Encoding: gzip, deflate, br
 9 Accept-Language: zh,zh-CN;q=0.8,ar;q=0.6,zh-TW;q=0.4
10 Cookie: BAIDUID=AE4D1DA6B2D6689BB8C557B3436893E3:FG=1; BIDUPSID=AE4D1DA6B2D6689BB8C557B3436893E3; PSTM=1501466227; BD_CK_SAM=1; PSINO=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BD_HOME=0; H_PS_PSSID=1420_25548_21080_20929; BD_UPN=12314353
11  

 

咱們要設置User-Agent模仿瀏覽器去訪問數據

 

 1 # _*_ coding:utf-8 _*_
 2 import urllib2
 3  
 4 # User-Agent是爬蟲與反爬蟲的第一步
 5 ua_headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'}
 6 # 經過urllib2.Request()方法構造一個請求對象
 7 request = urllib2.Request('http://www.baidu.com/',headers=ua_headers)
 8  
 9 #向指定的url地址發送請求,並返回服務器響應的類文件對象
10 response = urllib2.urlopen(request)
11  
12 # 服務器返回的類文件對象支持python文件對象的操做方法
13 # read()方法就是讀取文件裏的所有內容,返回字符串
14 html = response.read()
15  
16 print html
17  

 

Request總共三個參數,除了必需要有url參數,還有下面兩個:

1. data(默認空):是伴隨 url 提交的數據(好比要post的數據),同時 HTTP 請求將從 "GET"方式 改成 "POST"方式。

2. headers(默認空):是一個字典,包含了須要發送的HTTP報頭的鍵值對。

response的經常使用方法

 

 1 # _*_ coding:utf-8 _*_
 2 import urllib2
 3  
 4 # User-Agent是爬蟲與反爬蟲的第一步
 5 ua_headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'}
 6 # 經過urllib2.Request()方法構造一個請求對象
 7 request = urllib2.Request('http://www.baidu.com/',headers=ua_headers)
 8  
 9 #向指定的url地址發送請求,並返回服務器響應的類文件對象
10 response = urllib2.urlopen(request)
11  
12 # 服務器返回的類文件對象支持python文件對象的操做方法
13 # read()方法就是讀取文件裏的所有內容,返回字符串
14 html = response.read()
15  
16 # 返回HTTP的響應嗎,成功返回200,4服務器頁面出錯,5服務器問題
17 print response.getcode()     #200
18  
19 # 返回數據的實際url,防止重定向
20 print response.geturl()     #https://www.baidu.com/
21  
22 # 返回服務器響應的HTTP報頭
23 print response.info()
24  
25 # print html
26  

 

隨機選擇一個Use-Agent

爲了防止封IP,先生成一個user-agent列表,而後從中隨機選擇一個

 

 1 # _*_ coding:utf-8 _*_
 2 import urllib2
 3 import random
 4  
 5 url = 'http:/www.baidu.com/'
 6  
 7 # 能夠試User-Agent列表,也能夠是代理列表
 8 ua_list = ["Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
 9     "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
10     "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
11     "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
12     "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
13     "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
14     "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
15     "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
16     "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
17     "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
18     "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
19     "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
20     "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
21     "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
22     "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
23     "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
24     "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
25     "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
26 ]
27  
28 # 在User-Agent列表中隨機選擇一個User-Agent
29 user_agent = random.choice(ua_list)
30  
31 # 構造一個請求
32 request = urllib2.Request(url)
33  
34 # add_header()方法添加/修改一個HTTP報頭
35 request.add_header('User-Agent',user_agent)
36  
37 #get_header()獲取一個已有的HTTP報頭的值,注意只能第一個字母大寫,後面的要小寫
38 print request.get_header('User-agent')

 

urllib和urllib2的主要區別

 urllib和urllib2都是接受URL請求的相關模塊,可是提供了不一樣的功能,最顯著的區別以下:

(1)urllib僅能夠接受URL,不能建立,設置headers的request類實例;

(2)可是urllib提供urlencode()方法用來GET查詢字符串的產生,而urllib2則沒有(這是urllib和urllib2常常一塊兒使用的主要緣由)

(3)編碼工做使用urllib的urlencode()函數,幫咱們講key:value這樣的鍵值對轉換成‘key=value’這樣的字符串,解碼工做可使用urllib的unquote()

函數

 urllib.encode()的使用

urlencode()裏面必須是字典類型

 

 1 # _*_ coding:utf-8 _*_
 2 import urllib
 3  
 4 dic = {'derek':'編碼'}
 5 print urllib.urlencode(dic)    #derek=%E7%BC%96%E7%A0%81
 6  
 7 m = urllib.urlencode(dic)
 8  
 9 print urllib.unquote(m)         #derek=編碼
10  

 

通常HTTP請求提交數據,須要編碼成 URL編碼格式,而後作爲url的一部分,或者做爲參數傳到Request對象中。

GET請求通常用於咱們向服務器獲取數據,好比說,咱們用百度搜索知乎:https://www.baidu.com/s?wd=知乎

發現GEThttps://www.baidu.com/s?wd=%E7%9F%A5%E4%B9%8E,後面是一個長長的字符串,urldecode後發現就是知乎

 

 

用urllib.urlencode()進行轉碼,而後組合url

 

 1 # _*_ coding:utf-8 _*_
 2 import urllib,urllib2
 3  
 4 url = 'http://www.baidu.com/s'
 5 headers = {'UserAgent':'Mozilla'}
 6 keyword = raw_input('請輸入關鍵字:')
 7 wd = urllib.urlencode({'wd':keyword})
 8 fullurl = url + '?' + wd
 9 print fullurl
10 request = urllib2.Request(fullurl,headers=headers)
11 response = urllib2.urlopen(request)
12 print response.read()
13  

 

而後輸入關鍵字,爬取下對應的內容

 

爬取貼吧內容

 先了解貼吧url組成:

 

 

每一個貼吧url都是以'https://tieba.baidu.com/f?'開頭,而後是關鍵字 kw=‘’貼吧名字‘’,再後面是 &pn=頁數  (pn=0第一頁,pn=50第二頁,依次類推)

1.先寫一個main,提示用戶輸入要爬取的貼吧名,並用urllib.urlencode()進行轉碼,而後組合url

2.接下來,寫一個百度貼吧爬蟲接口tiebaSpider(),須要傳遞3個參數給這個接口, 一個是main裏組合的url地址,以及起始頁碼和終止頁碼,表示要爬取頁碼的範圍。

3.前面寫出一個爬取一個網頁的代碼。而後,將它封裝成一個小函數loadPage(),供咱們使用。

4.將爬取到的每頁的信息存儲在本地磁盤上,咱們能夠簡單寫一個存儲文件的接口writePage()

 

 1 # _*_ coding:utf-8 _*_
 2 import urllib,urllib2
 3  
 4 def loadPage(url,filename):
 5     #根據url發送請求,獲取服務器響應文件
 6     print '正在下載' + filename
 7     headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'}
 8     request = urllib2.Request(url,headers = headers)
 9     content = urllib2.urlopen(request).read()
10     return content
11 def writePage(html,filename):
12     #將html內容寫入到本地
13     print '正在保存' + filename
14     with open(unicode(filename,'utf-8'),'w') as f:
15         f.write(html)
16     print '_' * 30
17  
18 def tiebaSpider(url,beginPage,endPage):
19     #貼吧爬蟲調度器,負責組合處理每一個頁面的url
20     for page in range(beginPage,endPage + 1):
21         pn = (page - 1) * 50
22         filename = '' + str(page) + '頁.html'
23         fullurl = url + '&pn=' + str(pn)
24         # print fullurl
25         html = loadPage(fullurl,filename)
26         writePage(html,filename)
27  
28 if __name__ == '__main__':
29     kw = raw_input('請輸入貼吧名:')
30     beginPage = int(raw_input('請輸入起始頁:'))
31     endPage = int(raw_input('請輸入結束頁:'))
32  
33     url = 'https://tieba.baidu.com/f?'
34     key = urllib.urlencode({'kw':kw})
35     fullurl = url + key
36     tiebaSpider(fullurl,beginPage,endPage) 

 

經過輸入想要搜索的貼吧名字,爬取內容並保存到本地

 

 獲取Ajax方式加載的數據

爬蟲最須要關注的不是頁面信息,而是頁面信息的數據來源

Ajax方式加載的頁面,數據來源必定是JSON,直接對AJAX地址進行post或get,拿到JSON,就是拿到了網頁數據,

1)先經過瀏覽器訪問豆瓣電影排行榜

https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action=

 

 

2)瀏覽器訪問後,經過抓包工具就能夠獲取咱們想要的一些信息

 

 

只要response裏面有 JSON數據,咱們就能夠找到服務器的數據來源

分析發現變更的是start value和limit value, type,interval_id,action,固定不變,這三個url中已經包含了,因此formdata只用傳start和limit

 

 1 import urllib
 2 import urllib2
 3  
 4 url = 'https://movie.douban.com/typerank?type_name=%E5%89%A7%E6%83%85&type=11&interval_id=100:90&action='
 5 headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36'}
 6  
 7 # start和limit能夠本身隨便設置
 8 formdata = {'start':'20','limit':'100'}
 9  
10 data = urllib.urlencode(formdata)
11 request = urllib2.Request(url,data = data,headers=headers)
12  
13 response = urllib2.urlopen(request)
14 print response.read()
15  

 

     經歷了爬取豆瓣電影TOP250數據咱們會發現使用正則表達式其實並無多麼方便,有沒有更加好的工具呢?答案固然是有的。接下來將使用三個篇幅分別介紹XPath,Beautiful Soup和pyquery這三個解析庫。

 

XPath介紹

      XPath即爲XML路徑語言,它是一種用來肯定XML(標準通用標記語言的子集)文檔中某部分位置的語言。XPath基於XML的樹狀結構,有不一樣類型的節點,包括元素節點,屬性節點和文本節點,提供在數據結構樹中找尋節點的能力。 起初 XPath 的提出的初衷是將其做爲一個通用的、介於XPointer與XSLT間的語法模型。可是 XPath 很快的被開發者採用來看成小型查詢語言。*[來自360百科]*如今咱們使用它對HTML文檔進行搜索。

 

lxml的安裝

      lxml庫是Python的一個解析庫,支持HTML和XML的解析,支持XPath。下面介紹在Windows,Linux和Mac上的安裝。

 

Windows下的安裝

      首先使用命令`pip3 install lxml`進行安裝。若是沒有錯誤信息說明安裝成功了;若是出現錯誤,好比缺乏libxml2庫,使用wheel文件離線安裝。提供Win64位,Python3.6的lxml安裝包:https://pan.baidu.com/s/1wM1xKxCxOH8QOWclp6iasw。使用命令`pip3 install lxml-4.2.4-cp36-cp36m-win_amd64.whl`進行安裝。

Linux下的安裝

      首先也是使用命令`pip3 install lxml`進行安裝。若是沒有錯誤信息說明安裝成功了。若是報錯通常都是缺乏必要的庫,能夠參考如下解決方案。

Centos、Red Hat:

yum groupinstall -y development tools

yum install -y epel-release libxslt-devel libxml2-devel openssl-devel

 

Ubuntu、Debian和Deepin:

sudo apt-get install -y python3-dev build-essential libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev zlib1g-dev

1

安裝好這些必要的類庫後重試命令pip3 install lxml進行安裝。

 ## XPath經常使用規則 ##

表達式   描述

nodename   選擇這個節點名的全部子節點

/     從當前節點選擇直接子節點

//    從當前節點選取子孫節點

.      選擇當前節點

…    選取當前節點的父節點

@   選取屬性

標籤補全

如下是一段HTML:

 <div>

    <ul>

        <li class="item-0"><a href="www.baidu.com">baidu</a>

        <li class="item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>

        <li class="item-2"><a href="https://www.csdn.net/">csdn</a>

        <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>

顯然,這段HTML中的節點沒有閉合,咱們可使用lxml中的etree模塊進行補全。

 

 1 from lxml import etree
 2 
 3 text = '''
 4 
 5 <div>
 6 
 7     <ul>
 8 
 9         <li class="item-0"><a href="www.baidu.com">baidu</a>
10 
11         <li class="item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
12 
13         <li class="item-2"><a href="https://www.csdn.net/">csdn</a>
14 
15         <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
16 
17 '''
18 
19 html = etree.HTML(text)
20 
21 result = etree.tostring(html)
22 
23 print(result.decode('UTF-8'))

 

能夠看見etree不只將節點閉合了還添加了其餘須要的標籤。

除了直接讀取文本進行解析,etree也能夠讀取文件進行解析。

 

1 from lxml import etree
2 
3  
4 html = etree.parse('./test.html',etree.HTMLParser())
5 
6 result = etree.tostring(html)
7 
8 print(result.decode('UTF-8'))

 

獲取全部節點

      根據XPath經常使用規則能夠知道經過//能夠查找當前節點下的子孫節點,以上面的html爲例獲取全部節點。

 1 from lxml import etree
 2 
 3 html = etree.parse('./test.html',etree.HTMLParser())
 4 
 5 result = html.xpath('//*')#'//'表示獲取當前節點子孫節點,'*'表示全部節點,'//*'表示獲取當前節點下全部節點
 6 
 7 for item in result:
 8 
 9 print(item)
10 
11  

 

      若是咱們不要獲取全部節點而是指定獲取某個名稱的節點,只須要將*改成指定節點名稱便可。如獲取全部的li節點

 

 1 from lxml import etree
 2 
 3 html = etree.parse('./test.html',etree.HTMLParser())
 4 
 5 result = html.xpath('//li')#將*改成li,表示只獲取名稱爲li的子孫節點
 6 
 7 #返回一個列表
 8 
 9 for item in result:
10 
11 print(item)

 

獲取子節點

      根據XPath經常使用規則咱們可使用/或//獲取子孫節點或子節點。如今我要獲取li節點下的a節點。

1 from lxml import etree
2 
3 html = etree.parse('./test.html',etree.HTMLParser())
4 
5 result = html.xpath('//li/a')#//li選擇全部的li節點,/a選擇li節點下的直接子節點a
6 
7 for item in result:
8 
9     print(item)

 

咱們也可使用//ul//a首先選擇全部的ul節點,再獲取ul節點下的的全部a節點,最後結果也是同樣的。可是使用//ul/a就不行了,首先選擇全部的ul節點,再獲取ul節點下的直接子節點a,然而ul節點下沒有直接子節點a,固然獲取不到。須要深入理解//和/的不一樣之處。/用於獲取直接子節點,//用於獲取子孫節點。

根據屬性獲取

      根據XPath經常使用規則能夠經過@匹配指定的屬性。咱們經過class屬性找最後一個li節點。

 

from lxml import etree

html = etree.parse('./test.html',etree.HTMLParser())

result = html.xpath('//li[@class="item-3"]')#最後一個li的class屬性值爲item-3,返回列表形式

print(result)

 

獲取父節點

      根據XPath經常使用規則能夠經過..獲取當前節點的父節點。如今我要獲取最後一個a節點的父節點下的class屬性。

 

 1 from lxml import etree
 2 
 3 html = etree.parse('./test.html',etree.HTMLParser())
 4 
 5 result = html.xpath('//a[@href="https://hao.360.cn/?a1004"]/../@class')
 6 
 7 #a[@href="https://hao.360.cn/?a1004"]:選擇href屬性爲https://hao.360.cn/?a1004的a節點
 8 
 9 #..:選取父節點
10 
11 #@class:選取class屬性,獲取屬性值
12 
13 print(result)

 

獲取文本信息

      不少時候咱們找到指定的節點都是要獲取節點內的文本信息。咱們使用text()方法獲取節點中的文本。如今獲取全部a標籤的文本信息。

 

1 from lxml import etree
2 
3 html = etree.parse('./test.html',etree.HTMLParser())
4 
5 result = html.xpath('//ul//a/text()')
6 
7 print(result)

 

屬性多值匹配

      在上面的例子中全部的屬性值都只有一個,若是屬性值有多個還能匹配的上嗎?

 

 1 from lxml import etree
 2 
 3 text = '''
 4 
 5 <div>
 6 
 7     <ul>
 8 
 9         <li class="item-0"><a href="www.baidu.com">baidu</a>
10 
11         <li class="spitem-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
12 
13         <li class="item-2"><a href="https://www.csdn.net/">csdn</a>
14 
15         <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
16 
17 '''
18 
19 html = etree.HTML(text)
20 
21 result = html.xpath('//li[@class="sp"]')
22 
23 print(result)
24 
25  

 

第二個li節點的class屬性有兩個值:sp和item-1。若是咱們的xpath匹配規則爲//li[@class="sp"]匹配的僅僅是class屬性值只爲sp的li節點,這顯然是不存在的。

      遇到屬性值有多個的狀況咱們須要使用contains()函數了,contains()匹配一個屬性值中包含的字符串 。包含的字符串,而不是某個值。

 

 1 from lxml import etree
 2 
 3 text = '''
 4 
 5 <div>
 6 
 7     <ul>
 8 
 9         <li class="item-0"><a href="www.baidu.com">baidu</a>
10 
11         <li class="sp item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
12 
13         <li class="item-2"><a href="https://www.csdn.net/">csdn</a>
14 
15         <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
16 
17 '''
18 
19 html = etree.HTML(text)
20 
21 result = html.xpath('//li[contains(@class,"sp")]/a/text()')
22 
23 print(result)

 

多屬性匹配

      屬性多值匹配是節點屬性有許多個值,咱們根據一個值獲取符合添加的節點。因爲咱們不少狀況下沒法僅僅根據一個屬性值就獲取到目標節點,每每要根據多個屬性來獲取目標節點。

 

 1 from lxml import etree
 2 
 3 text = '''
 4 
 5 <div>
 6 
 7     <ul>
 8 
 9         <li class="sp item-0" name="one"><a href="www.baidu.com">baidu</a>
10 
11 '''
12 
13 html = etree.HTML(text)
14 
15 result = html.xpath('//li[contains(@class,"item-0") and @name="one"]/a/text()')#使用and操做符將兩個條件相連。
16 
17 print(result)
18 
19 也許你會說這個直接使用name的屬性值就能夠獲得了,然而,這裏只是做爲演示。
20 
21  
22 
23 from lxml import etree
24 
25  
26 
27 text = '''
28 
29 <div>
30 
31     <ul>
32 
33         <li class="sp item-0" name="one"><a href="www.baidu.com">baidu</a>
34 
35         <li class="sp item-1" name="two"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
36 
37         <li class="sp item-2" name="two"><a href="https://www.csdn.net/">csdn</a>
38 
39         <li class="sp item-3" name="four"><a href="https://hao.360.cn/?a1004">hao123</a>
40 
41 '''
42 
43  
44 
45 html = etree.HTML(text)
46 
47 result = html.xpath('//li[2]/a/text()')#選擇第二個li節點,獲取a節點的文本
48 
49 print(result)
50 
51 result = html.xpath('//li[last()]/a/text()')#選擇最後一個li節點,獲取a節點的文本
52 
53 print(result)
54 
55 result = html.xpath('//li[last()-1]/a/text()')#選擇倒數第2個li節點,獲取a節點的文本
56 
57 print(result)
58 
59 result = html.xpath('//li[position()<=3]/a/text()')#選擇前三個li節點,獲取a節點的文本
60 
61 print(result)

 

咱們使用了last()和postion()函數,在XPath中還有不少函數,詳情見:w3school 函數。

 

XPath 軸

      咱們能夠經過XPath獲取祖先節點,屬性值,兄弟節點等等,這就是XPath的節點軸。軸可定義相對於當前節點的節點集。

 

軸名稱   結果

ancestor      選取當前節點的全部先輩(父、祖父等)。

ancestor-or-self 選取當前節點的全部先輩(父、祖父等)以及當前節點自己。

attribute      選取當前節點的全部屬性。

child      選取當前節點的全部直接子元素。

descendant  選取當前節點的全部後代元素(子、孫等)。

descendant-or-self   選取當前節點的全部後代元素(子、孫等)以及當前節點自己。

following     選取文檔中當前節點的結束標籤以後的全部節點。

following-sibling       選取當前節點以後的全部同級節點。

namespace  選取當前節點的全部命名空間節點。

parent   選取當前節點的父節點。

preceding    選取文檔中當前節點的開始標籤以前的全部同級節點及同級節點下的節點。

preceding-sibling      選取當前節點以前的全部同級節點。

self 選取當前節點。

【上表來源:[w3school XPath軸](https://www.w3cschool.cn/xpath/xpath-axes.html)】

使用示例:

 

 1 from lxml import etree
 2 
 3 text = '''
 4 
 5 <div>
 6 
 7     <ul>
 8 
 9         <li class="sp item-0" name="one"><a href="www.baidu.com">baidu</a>
10 
11         <li class="sp item-1" name="two"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
12 
13         <li class="sp item-2" name="two"><a href="https://www.csdn.net/">csdn</a>
14 
15         <li class="sp item-3" name="four"><a href="https://hao.360.cn/?a1004">hao123</a>
16 
17 '''
18 
19 html = etree.HTML(text)
20 
21 result = html.xpath('//li[1]/ancestor::*')#ancestor表示選取當前節點祖先節點,*表示全部節點。合:選擇當前節點的全部祖先節點。
22 
23 print(result)
24 
25 result = html.xpath('//li[1]/ancestor::div')#ancestor表示選取當前節點祖先節點,div表示div節點。合:選擇當前節點的div祖先節點。
26 
27 print(result)
28 
29 result =  html.xpath('//li[1]/ancestor-or-self::*')#ancestor-or-self表示選取當前節點及祖先節點,*表示全部節點。合:選擇當前節點的全部祖先節點及本及自己。
30 
31 print(result)
32 
33 result =  html.xpath('//li[1]/attribute::*')#attribute表示選取當前節點的全部屬性,*表示全部節點。合:選擇當前節點的全部屬性。
34 
35 print(result)
36 
37 result =  html.xpath('//li[1]/attribute::name')#attribute表示選取當前節點的全部屬性,name表示name屬性。合:選擇當前節點的name屬性值。
38 
39 print(result)
40 
41 result =  html.xpath('//ul/child::*')#child表示選取當前節點的全部直接子元素,*表示全部節點。合:選擇ul節點的全部直接子節點。
42 
43 print(result)
44 
45 result =  html.xpath('//ul/child::li[@name="two"]')#child表示選取當前節點的全部直接子元素,li[@name="two"]表示name屬性值爲two的li節點。合:選擇ul節點的全部name屬性值爲two的li節點。
46 
47 print(result)
48 
49 result =  html.xpath('//ul/descendant::*')#descendant表示選取當前節點的全部後代元素(子、孫等),*表示全部節點。合:選擇ul節點的全部子節點。
50 
51 print(result)
52 
53 result =  html.xpath('//ul/descendant::a/text()')#descendant表示選取當前節點的全部後代元素(子、孫等),a/test()表示a節點的文本內容。合:選擇ul節點的全部a節點的文本內容。
54 
55 print(result)
56 
57 result =  html.xpath('//li[1]/following::*')#following表示選取文檔中當前節點的結束標籤以後的全部節點。,*表示全部節點。合:選擇第一個li節點後的全部節點。
58 
59 print(result)
60 
61 result =  html.xpath('//li[1]/following-sibling::*')#following-sibling表示選取當前節點以後的全部同級節點。,*表示全部節點。合:選擇第一個li節點後的全部同級節點。
62 
63 print(result)
64 
65 result =  html.xpath('//li[1]/parent::*')#選取當前節點的父節點。父節點只有一個,祖先節點可能多個。
66 
67 print(result)
68 
69 result =  html.xpath('//li[3]/preceding::*')#preceding表示選取文檔中當前節點的開始標籤以前的全部同級節點及同級節點下的節點。,*表示全部節點。合:選擇第三個li節點前的全部同級節點及同級節點下的子節點。
70 
71 print(result)
72 
73 result =  html.xpath('//li[3]/preceding-sibling::*')#preceding-sibling表示選取當前節點以前的全部同級節點。,*表示全部節點。合:選擇第三個li節點前的全部同級節點。
74 
75 print(result)
76 
77 result =  html.xpath('//li[3]/self::*')#選取當前節點。
78 
79 print(result)

 

XPath Helper插件

      實話說我不想寫XPath的匹配規則,在真正的網頁解析中怎麼可能那麼短的規則。這時候咱們就可使用Chrome的插件XPath Helper了【下載地址】,使用它咱們能夠很快速的獲得匹配規則。直接將下載下來的crx文件拖進Chrome擴展程序界面安裝便可。

 

出現紅框內圖標說明安裝成功了。

運行XPath Helper插件,安裝shift選擇咱們須要的內容,自動生成匹配規則。

 

正則表達式

1、簡介

  正則表達式,又稱正規表示式、正規表示法、正規表達式、規則表達式、常規表示法(英語:Regular Expression,在代碼中常簡寫爲regex、regexp或RE),計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列匹配某個句法規則的字符串。在不少文本編輯器裏,正則表達式一般被用來檢索、替換那些匹配某個模式的文本。

  compile 函數根據一個模式字符串和可選的標誌參數生成一個正則表達式對象。該對象擁有一系列方法用於正則表達式匹配和替換。

2、使用方法

  一、簡單看一下寫法

  函數語法:

  re.match(pattern, string, flags=0)

  

1

2

3

4

5

6

7

8

9

10

函數參數說明:

參數  描述

pattern 匹配的正則表達式

string  要匹配的字符串。

flags   標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫,多行匹配等等。<br>

  匹配成功re.match方法返回一個匹配的對象,不然返回None。

  咱們可使用group(num) 或 groups() 匹配對象函數來獲取匹配表達式。<br>

匹配對象方法  描述

group(num=0)    匹配的整個表達式的字符串,group() 能夠一次輸入多個組號,在這種狀況下它將返回一個包含那些組所對應值的元組。

groups()    返回一個包含全部小組字符串的元組,從 1 到 所含的小組號。

  

 1 import re
 2 
 3  
 4 
 5 p = re.compile('abcd')
 6 
 7 print(type(p))
 8 
 9 #<class '_sre.SRE_Pattern'>
10 
11 print(dir(p))
12 
13 #['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'findall', 'finditer', 'flags', 'fullmatch', 'groupindex', 'groups', 'match', 'pattern', 'scanner', 'search', 'split', 'sub', 'subn']
14 
15 m = p.match('abcdef')
16 
17 print(type(m))
18 
19 #<class '_sre.SRE_Match'>
20 
21 print(dir(m))
22 
23 #['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
24 
25 print(m.group())
26 
27 #abcd
28 
29 print(m.group(0))
30 
31 #abcd

 

  re.match()方法值匹配字符串的開頭若是不知足,就返回一個None

1

2

3

4

5

6

import re

 

p = re.compile('abcd')

m = p.match('abdcef')

print(m.group())

#AttributeError: 'NoneType' object has no attribute 'group'

  

  神奇的.

1

2

3

4

5

6

import re

 

p = re.compile('.')

m = p.match('abdcef')

print(m.group())

#a

  

  特殊字符(元字符)

  注意:\在裏面是轉義詞的意思,例如,你想匹配一個re.compile('.'),這個.是匹配任意字符。可是我就想讓它匹配一個.怎麼辦,re.compile('\.'),這樣的話它就真的只匹配一個點。

 

  re.findall()

1

2

3

4

5

6

7

8

9

10

import re

 

p = re.compile('\.')

#匹配符號點(注意這裏可前面加上了轉義符\,就不是匹配任意字符了)

m = p.findall('abc.def.')

print(type(m))

#<class 'list'>

print(m)

#['.', '.']

#能夠得出,返回的是一個list,全局查找,不想match方法只匹配字符串開頭

  

   數量詞:

 

 

   貪婪模式和非貪婪模式

  一、一個小例子

  咱們知道*表示匹配一個字符串0次或者屢次,而+是匹配字符串1次或屢次,因此*的時候匹配了0次也打印出來了,而+只找匹配1次的字符。

1

2

3

4

5

6

7

8

9

import re

 

p = re.compile('[abc]+')

m = p.findall('abcdef')

print(m)

#*

    #['abc', '', '', '', '']

#+

    #['abc']

  

  貪婪與非貪婪模式影響的是被量詞修飾的子表達式的匹配行爲,貪婪模式在整個表達式匹配成功的前提下,儘量多的匹配.

  而非貪婪模式在整個表達式匹配成功的前提下,儘量少的匹配。

 

   二、{}

  根據下面的例子不難看出,數量詞{m}表示的是匹配前面字符串的幾個字符串

1

2

3

4

5

6

7

8

9

10

11

import re

 

p = re.compile('[abc]{3}')

m = p.findall('abcdabcd')

print(m)

#{1}

    #['a', 'b', 'c', 'a', 'b', 'c']

#{2}

    #['ab', 'ab']

#{3}

    #['abc', 'abc']

  

   

  re.search方法

  re.search 掃描整個字符串並返回第一個成功的匹配,不然返回的是None(注意findall返回的是list,而search返回的直接就是字符串)

1

2

3

4

5

import re

 

p = re.compile('abcd')

m = p.search('abcdabcd')

print(m.group())

 

  檢索和替換

   Python 的 re 模塊提供了re.sub用於替換字符串中的匹配項。

 語法:
 

1

re.sub(pattern, repl, string, count=0, flags=0)

參數:

  • pattern : 正則中的模式字符串。
  • repl : 替換的字符串,也可爲一個函數。
  • string : 要被查找替換的原始字符串。
  • count : 模式匹配後替換的最大次數,默認 0 表示替換全部的匹配。

 

 示例一:
 替換一#開頭的全部字符(匹配任意字符0次或者屢次),替換成無。

1

2

3

4

5

import re

 

phone = "2004-959-559 # 這是一個國外電話號碼"

num = re.sub('#.*','',phone)

print(num)

  示例一:擴展

  找到全部非數字的字符,‘’表明着刪除。

1

2

3

4

5

import re

 

phone = "2004-959-559 # 這是一個國外電話號碼"

num = re.sub('\D','',phone)

print(num)

  

  正則表達式修飾符 - 可選標誌

   正則表達式能夠包含一些可選標誌修飾符來控制匹配的模式。修飾符被指定爲一個可選的標誌。多個標誌能夠經過按位 OR(|) 它們來指定。如 re.I | re.M 被設置成 I 和 M 標誌:

   

 

如今擁有了正則表達式這把神兵利器,咱們就能夠進行對爬取到的所有網頁源代碼進行篩選了。

下面咱們一塊兒嘗試一下爬取內涵段子網站:
http://www.neihan8.com/article/list_5_1.html

打開以後,不難看出裏面一個一個很是有內涵的段子,當你進行翻頁的時候,注意url地址的變化:

  • 第一頁url: http: //www.neihan8.com/article/list_5_1 .html
  • 第二頁url: http: //www.neihan8.com/article/list_5_2 .html
  • 第三頁url: http: //www.neihan8.com/article/list_5_3 .html
  • 第四頁url: http: //www.neihan8.com/article/list_5_4 .html

這樣咱們的url規律找到了,要想爬取全部的段子,只須要修改一個參數便可。
咱們就開始一步一步將全部的段子爬取下來吧。

第一步:獲取數據

1. 按照咱們以前的用法,咱們須要一個加載頁面的方法。

這裏咱們統必定義一個類,將url請求做爲一個成員方法處理。
咱們建立了一個文件,叫duanzi_spider.py
而後定義一個Spider類,而且添加一個加載頁面的成員方法。

 1 import urllib2
 2  
 3 class Spider:
 4     """
 5         內涵段子爬蟲類
 6     """
 7     def loadPage(self, page):
 8         """
 9             @brief 定義一個url請求網頁的方法
10             @param page須要請求的第幾頁
11             @returns 返回的頁面url
12         """
13         url = "http://www.neihan8.com/article/list_5_" + str(page)+ ".html"
14         #user-Agent頭
15         user_agent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT6.1; Trident/5.0"
16         headers = {"User-Agent":user_agent}
17         req = urllib2.Request(url, headers = headers)
18         response = urllib2.urlopen(req)
19         print html

 

以上的loadPage的實現思想一想必你們都應該熟悉了,須要注意定義python類的成員方法須要額外添加一個參數self.

2.寫main函數測試一個loadPage方法

 1 if __name__ == "__main__":
 2     """
 3         =====================
 4             內涵段子小爬蟲
 5         =====================
 6     """
 7     print("請按下回車開始")
 8     raw_input()
 9     
10     #定義一個Spider對象
11     mySpider = Spider()
12     mySpider.loadPage(1)

 

  • 程序正常執行的話,咱們會在皮姆上打印了內涵段子第一頁的所有html代碼。可是咱們發現,html中的中文部分顯示的多是亂碼。

    那麼咱們須要簡單的將獲得的網頁源代碼處理一下:
 1 def loadPage(self, page):
 2     """
 3         @bridf 定義一個url請求網頁的方法
 4         @param page 須要請求的第幾頁
 5         @returns 返回的頁面html
 6     """
 7  
 8     url = "http://www.neihan8.com/article/list_5_"+str(page)+".html"
 9     #user-agent頭
10     user-agent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT6.1; Trident/5.0"
11     headers = {"User-Agent":user-agent}
12     req = urllib2.Request(url, headers = headers)
13     response = urllib2.urlopen(req)
14     html = response.read()
15     gbk_html = html.decode("gbk").encode("utf-8")
16  
17     return gbk_html

 

注意:對於每一個網站對中文的編碼各自不一樣,因此html.decode("gbk")的寫法並非通用的,根據網站的編碼而異。

 

第二步:篩選數據

接下來咱們已經獲得了整個頁面的數據。可是,不少內容咱們並不關心,因此下一步咱們須要篩選數據。如何篩選,就用到了上一節講述的正則表達式

  • 首先
  • 而後,咱們獲得的gbk_html中進行篩選匹配。
importre

咱們須要一個匹配規則

咱們能夠打開內涵段子的網頁,鼠標點擊右鍵"查看源代碼"你會驚奇的發現,咱們須要的每一個段子的內容都是在一個<div>標籤中,並且每一個div標籤都有一個屬性class="f18 mb20"

根據正則表達式,咱們能夠推算出一個公式是:

<div.*?class="f18 mb20"></div>(.*?)
  • 這個表達式實際上就是匹配到全部divclass="f18 mb20"裏面的內容(具體能夠看前面介紹)
  • 而後這個正則應用到代碼中,咱們會獲得如下代碼:
  • 這裏須要注意一個是re.S是正則表達式中匹配的一個參數。
  • 若是沒有re.S則是隻匹配一行有沒有符合規則的字符串,若是沒有則下一行從新匹配。
  • 若是加上re.S則是將全部的字符串按一個總體進行匹配,findall將匹配到的全部結果封裝到一個list中。
    • 若是咱們寫了一個遍歷item_list的一個方法printOnePage()。ok程序寫到這,咱們再一次執行一下。
 1 def loadPage(self, page):
 2     """
 3         @brief 定義一個url請求網頁的辦法
 4         @param page 須要請求的第幾頁
 5         @returns 返回的頁面html
 6     """
 7     url = "http://www.neihan8.com/article/list_5_" +str(page) + ".html"
 8     #User-Agent頭
 9     user-agent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT6.1; Trident/5.0"  
10  
11     headers = {"User-Agent":user-agent}
12     req = urllib2.Request(url, headers=headers)
13     response = urllib2.urlopen(req)
14  
15     html = response.read()
16  
17     gbk_html = html.decode("gbk").encode("utf-8")
18  
19     #找到全部的段子內容<div class="f18 mb20"></div>
20     #re.S 若是沒有re.S,則是隻匹配一行有沒有符合規則的字符串,若是沒有則匹配下一行從新匹配
21     #若是加上re.S,則是將全部的字符串按一個總體進行匹配
22     pattern = re.compile(r'<div.*?class="f18 mb20">(.*?)</div>', re.S)
23     item_list = pattern.findall(gbk_html)
24  
25     return item_list
26  
27 def printOnePage(self, item_list, page):
28     """
29         @brief 處理獲得的段子列表
30         @param item_list 獲得的段子列表
31         @param page處理第幾頁
32     """
33  
34     print("*********第%d頁,爬取完畢...******"%page)
35  
36     for item in item_list:
37         print("===============")
38         print ite
39 python duanzi_spider.py

 

咱們第一頁的所有段子,不包含其餘信息所有的打印了出來.

  • 你會發現段子中有不少<p>,</p>非常不舒服,實際上這個是html的一種段落的標籤。
  • 在瀏覽器上看不出來,可是若是按照文本打印會有<p>出現,那麼咱們只須要把咱們的內容去掉便可。
  • 咱們能夠以下簡單修改一下printOnePage()
  • 咱們能夠將全部的段子存放在文件中。好比,咱們能夠將獲得的每一個item不是打印出來,而是放在一個叫duanzi.txt的文件中也能夠。
  • 而後咱們將全部的print的語句改寫成writeToFile(), 當前頁面的全部段子就存在了本地的duanzi.txt文件中。
  • 接下來咱們就經過參數的傳遞對page進行疊加來遍歷內涵段子吧的所有段子內容。
  • 只須要在外層加上一些邏輯處理便可。
  • 最後,咱們執行咱們的代碼,完成後查看當前路徑下的duanzi.txt文件,裏面已經有了咱們要的內涵段子。
 1 def printOnePage(self, item_list, page):
 2     """
 3         @brief 處理獲得的段子列表
 4         @param item_list 獲得的段子列表
 5         @param page 處理第幾頁
 6     """
 7     print("******第%d頁,爬取完畢*****"%page)  
 8     for item in item_list:
 9         print("============")
10         item = item.replace("<p>", "").replace("</p>", "").replace("<br />", "")
11         print item

 

第三步:保存數據

 1 def writeToFile(self, text):
 2     """
 3         @brief 將數據追加寫進文件中
 4         @param text 文件內容
 5     """
 6  
 7     myFile = open("./duanzi.txt", "a")  #a追加形式打開文件  
 8     myFile.write(text)
 9     myFile.write("-------------------------")
10     myFile.close()
11 def printOnePage(self, item_list, page):
12     """
13         @brief  處理獲得的段子列表
14         @param item_list 獲得的段子列表
15         @param page 處理第幾頁
16     """
17  
18     print("***第%d頁,爬取完畢****"%page)
19     for item in item_list:
20         item = item.replace("<p>", "").replace("</p>", "").replace("<br />". "")
21  
22         self.writeToFile(item)

 

第四步:顯示數據

 1 def doWork(self):
 2     """
 3         讓爬蟲開始工做
 4     """
 5     while self.enable:
 6         try:
 7             item_list = self.loadPage(self.page)
 8         except urllib2.URLError, e:
 9             print e.reason
10             continue
11  
12     #將獲得的段子item_list處理
13     self.printOnePage(item_list, self.page)
14     self.page += 1
15     print "按回車繼續...."
16     print "輸入quit退出"
17  
18     command = raw_input()
19     if(command == "quit"):
20         self.enable = False
21         break

 

公告

 

更多python源碼,視頻教程,歡迎關注公衆號:南城故夢

>零起點大數據與量化分析PDF及教程源碼
>利用python進行數據分析PDF及配套源碼
>大數據項目實戰之Python金融應用編程(數據分析、訂價與量化投資)講義及源碼
>董付國老師Python教學視頻
1. 課堂教學管理系統開發:在線考試功能設計與實現
2. Python+pillow圖像編程;
3. Python+Socket編程
4. Python+tkinter開發;
5. Python數據分析與科學計算可視化
6. Python文件操做
7. Python多線程與多進程編程
8. Python字符串與正則表達式
.....

>數據分析教學視頻
1. 輕鬆駕馭統計學——數據分析必備技能(12集);
2. 輕鬆上手Tableau 軟件——讓數據可視化(9集);
3. 競品分析實戰攻略(6集);
4. 電商數據化運營——三大數據化工具應用(20集);

>大數據(視頻與教案)
1. hadoop
2. Scala
3. spark

>Python網絡爬蟲分享系列教程PDF

>【千鋒】Python爬蟲從入門到精通(精華版)(92集)

歡迎關注公衆號獲取學習資源:南城故夢

相關文章
相關標籤/搜索