專欄目錄: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
HTTP和HTTPS
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語法結構,完整的顯示出來了。
URL(Uniform / Universal Resource Locator的縮寫)
定義:統一資源定位符,是用於完整地描述Internet上網頁和其餘資源的地址的一種標識方法。
基本格式:scheme://host[:port#]/path/…/[?query-string][#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 回顯服務器收到的請求,主要用於測試或診斷。
主要方法get和post請求
http://www.baidu.com/s?wd=Chinese
HTTP響應狀態碼
瀏覽器內核
瀏覽器 內核
IE Trident
Chrome Webkit
Firefox Gecho
Opera Pesto
Safari(Apple) Webkit
HTTP代理工具Fiddler
Fiddler是一款強大Web調試工具,它能記錄全部客戶端和服務器的HTTP請求.
Request部分詳解
Responser部分詳解
瞭解了這些知識後,接下來真正邁向爬蟲之路.......
所謂網頁抓取,就是把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都是接受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
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方式加載的頁面,數據來源必定是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選擇咱們須要的內容,自動生成匹配規則。
正則表達式,又稱正規表示式、正規表示法、正規表達式、規則表達式、常規表示法(英語:Regular Expression,在代碼中常簡寫爲regex、regexp或RE),計算機科學的一個概念。正則表達式使用單個字符串來描述、匹配一系列匹配某個句法規則的字符串。在不少文本編輯器裏,正則表達式一般被用來檢索、替換那些匹配某個模式的文本。
compile 函數根據一個模式字符串和可選的標誌參數生成一個正則表達式對象。該對象擁有一系列方法用於正則表達式匹配和替換。
一、簡單看一下寫法
函數語法:
re.match(pattern, string, flags=0)
1 2 3 4 5 6 7 8 9 10 |
|
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 |
|
神奇的.
1 2 3 4 5 6 |
|
特殊字符(元字符)
注意:\在裏面是轉義詞的意思,例如,你想匹配一個re.compile('.'),這個.是匹配任意字符。可是我就想讓它匹配一個.怎麼辦,re.compile('\.'),這樣的話它就真的只匹配一個點。
1 2 3 4 5 6 7 8 9 10 |
|
數量詞:
貪婪模式和非貪婪模式
一、一個小例子
咱們知道*表示匹配一個字符串0次或者屢次,而+是匹配字符串1次或屢次,因此*的時候匹配了0次也打印出來了,而+只找匹配1次的字符。
1 2 3 4 5 6 7 8 9 |
|
貪婪與非貪婪模式影響的是被量詞修飾的子表達式的匹配行爲,貪婪模式在整個表達式匹配成功的前提下,儘量多的匹配.
而非貪婪模式在整個表達式匹配成功的前提下,儘量少的匹配。
二、{}
根據下面的例子不難看出,數量詞{m}表示的是匹配前面字符串的幾個字符串
1 2 3 4 5 6 7 8 9 10 11 |
|
re.search 掃描整個字符串並返回第一個成功的匹配,不然返回的是None(注意findall返回的是list,而search返回的直接就是字符串)
1 2 3 4 5 |
|
Python 的 re 模塊提供了re.sub用於替換字符串中的匹配項。
語法:
1 |
|
參數:
示例一:
替換一#開頭的全部字符(匹配任意字符0次或者屢次),替換成無。
1 2 3 4 5 |
|
示例一:擴展
找到全部非數字的字符,‘’表明着刪除。
1 2 3 4 5 |
|
正則表達式能夠包含一些可選標誌修飾符來控制匹配的模式。修飾符被指定爲一個可選的標誌。多個標誌能夠經過按位 OR(|) 它們來指定。如 re.I | re.M 被設置成 I 和 M 標誌:
如今擁有了正則表達式這把神兵利器,咱們就能夠進行對爬取到的所有網頁源代碼進行篩選了。
下面咱們一塊兒嘗試一下爬取內涵段子網站:
http://www.neihan8.com/article/list_5_1.html
打開以後,不難看出裏面一個一個很是有內涵的段子,當你進行翻頁的時候,注意url地址的變化:
這樣咱們的url規律找到了,要想爬取全部的段子,只須要修改一個參數便可。
咱們就開始一步一步將全部的段子爬取下來吧。
這裏咱們統必定義一個類,將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.
1 if __name__ == "__main__": 2 """ 3 ===================== 4 內涵段子小爬蟲 5 ===================== 6 """ 7 print("請按下回車開始") 8 raw_input() 9 10 #定義一個Spider對象 11 mySpider = Spider() 12 mySpider.loadPage(1)
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")的寫法並非通用的,根據網站的編碼而異。
接下來咱們已經獲得了整個頁面的數據。可是,不少內容咱們並不關心,因此下一步咱們須要篩選數據。如何篩選,就用到了上一節講述的正則表達式
importre
咱們能夠打開內涵段子的網頁,鼠標點擊右鍵"查看源代碼"你會驚奇的發現,咱們須要的每一個段子的內容都是在一個<div>
標籤中,並且每一個div
標籤都有一個屬性class="f18 mb20"
<div.*?class="f18 mb20"></div>(.*?)
div
中class="f18 mb20"
裏面的內容(具體能夠看前面介紹)re.S
是正則表達式中匹配的一個參數。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>
出現,那麼咱們只須要把咱們的內容去掉便可。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
>零起點大數據與量化分析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集)