python 獲取網絡數據也很方便html
抓取python
requests 第三方庫適合作中小型網絡爬蟲的開發, 大型的爬蟲須要用到 scrapy 框架正則表達式
解析express
BeautifulSoup 庫, re 模塊json
(一) requests 庫網絡
基本方法: requests.get() : 請求獲取指定URL位置的資源, 對應http 協議的get方法框架
注意: 在抓取網頁前要看一看這個網站是否是有爬蟲協議, scrapy
如何看網站的爬蟲協議? 有的網站會提供robots.txt函數
例如豆瓣的 www.douban.com/robots.txt學習
實例: 抓取豆瓣上小王子的一個書評
咱們要抓取subject 目錄, 從上述協議中看到它沒有被禁止
抓取多個頁面要注意他的延時, 這裏是5s
import requests r=requests.get('http://book.douban.com/subject/1084336/comments/') print(r.status_code) # 200 則說明一切正常 print(r.text) # 獲取頁面內容
除了上述r.text 解碼, 還有r.content, r.json 等
上述抓取的數據有不少如下結構
<p class="comment-content">
<span class="short">痛苦迷茫不是由於成爲了「好笑」的大人,而是成爲大人卻沒有真正長大。因此回過頭來想要從懷念童年中解脫緩解痛苦那是本末倒置的作法。若是你做爲一個成年人以爲痛苦,緣由不是由於你「成年」了,也不是由於是生而爲「人」,而是「你」中止了思考中止了學習</p>
能夠用beautifulsoup進行解析, 舉例來看
from bs4 import BeautifulSoup markup='<p class="title"><b>The Little Prince</b></p>' soup=BeautifulSoup(markup,'lxml') # 對於html使用lxml解析器較好 # soup 的對象有四種: tag , navigablestring, beautifulsoup, comment #tag就是標籤, 相似</b>**</b> 對文字內容的修飾 #navigablestring就是tag 中的字符串, 好比這裏的The Little Prince #comment 是navigablestring的子類 print(soup.b) # 標籤b的內容: <b>The Little Prince</b> print(type(soup.b)) # <class 'bs4.element.Tag'> # tag 也有兩個屬性 name ,attrs tag=soup.p print(tag.name) # p print(tag.attrs) # {'class': ['title']} print(tag.string) # 取到了tag中包含的非屬性的字符串:The Little Prince print(type(tag.string)) # <class 'bs4.element.NavigableString'>
soup.find_all('b') # 找到全部b標籤的內容
Out[24]: [<b>The Little Prince</b>]
小王子的解析
import requests from bs4 import BeautifulSoup r=requests.get('http://book.douban.com/subject/1084336/comments/') soup=BeautifulSoup(r.text,'lxml') # 得到beautifulsoup 對象soup pattern=soup.find_all('p','comment-content')
#評論的屬性是標籤p,屬性是comment-content, 返回列表
for item in pattern: print(item.string)
沒有結果, 全是none!! 爲啥??
(二)regular expression
正則表達式能夠簡化字符串, 'py','pyy','pyyy','pyyy....' 這些均可以用 正則表達式py+ 表示
經常使用表達式:
補充: \D 表示非數字字符 {m,} 至少擴展m次, {:m}擴展0-m次
\b :匹配一個單詞邊界,也就是指單詞和空格間的位置(即正則表達式的「匹配」有兩種概念,一種是匹配字符,一種是匹配位置,這裏的\b就是匹配位置的)。例如,「er\b」能夠匹配「never」中的「er」,但不能匹配「verb」中的「er」。
\B :匹配非單詞邊界。「er\B」能匹配「verb」中的「er」,但不能匹配「never」中的「er」。
舉例:
p(y|yt|yth|ytho)?n 表示: ?表示擴展0次或者1次, 表示爲pn,pyn,pytn,pythn,python
python+ 表示: python ,pythonn,pythonnn,....
py[th]on 表示:pyton ,pyhon
py[^th]?on 表示:pyon,pyaon,pybon,....,其中排除 t h的字母進行0次或1次擴展
py{:3}on 表示pon pyon pyyon pyyyon (擴展y 0 1 2 3次)
^[A-Za-z]+$ 匹配字符串開頭和結尾 ,表示 由26個字母組成的字符串
^[A-Za-z0-9]+$ 匹配字符串開頭和結尾, 表示 由26個字母和10個數字組成的字符串
^-?\d+$ 整數形式的字符串(多是負數)
^[0-9]*[1-9][0-9]*$ 正整數形式的字符串
[1-9]\d{5} 中國境內郵政編碼(6位數)
[\u4e00-\u9fa5] 匹配中文字符
\d{3}-\d{8} 或者 \d{4}-\d{7} 表示國內電話號碼: 010-62914227
舉例: 匹配IP地址的正則表達式:
一個IP地址分爲 四段,每段是0-255
\d+.\d+.\d+.\d+ 這個沒有限制位數 :+能夠擴展1次或者無限次
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3} 這個有限制位數 每次是1 2 3位 ,但仍是不精確 300.300.300.300也能夠匹配
0-99表示爲[0-9]?\d 100-199 表示爲 1\d{2} 200-249 表示爲2[0-4]\d 250-255 表示爲25[0-5]
所示0-255由上面四段取| (或)獲得,0-255表示爲(([0-9]?\d |1\d{2}|2[0-4]\d|25[0-4]).){3}([0-9]?\d |1\d{2}|2[0-4]\d|25[0-4])
(二)re庫介紹
re 是python的標準庫,主要功能是用於字符串的匹配
import re 調用
正則表達式的表示類型:re庫採用了raw string類型表示正則表達式,表示爲r'text'
舉例: r'[1-9]\d{5}' 大陸郵政編碼
r'\d{3}-\d{8}|\d{4}-\d{7}' 國內電話號碼
原生字符串與普通字符串不一樣在於 前面加一個r
區別在於原生字符串不包含轉義符 \的 ,原生字符串中的\ 不解釋爲轉義符
r'[1-9]\d{5}' 用普通字符串表示爲:'[1-9]\\d{5}' 要用雙斜槓\\
具體介紹上述函數:
(1) re.search(pattern, string, flags=0), pattern 表示正則表達式的字符串或者原生字符串
string表示待匹配字符串 ,flags表示一些控制標記
舉例:
import re match=re.search(r'[1-9]\d{5}','BIT 100081') print(match) print(match.group(0)) ans: <_sre.SRE_Match object; span=(4, 10), match='100081'> 100081
(2)re.match(pattern, string, flags=0)
import re match=re.match(r'[1-9]\d{5}','BIT 100081') print(match) print(match.group(0)) ans:AttributeError: 'NoneType' object has no attribute 'group'
報錯了,由於這個字符串的開頭不匹配
若要對匹配結果進行使用,爲了防止報錯,增長if 判斷
import re match=re.match(r'[1-9]\d{5}','BIT 100081') if match: print(match.group(0))
此時不會報錯,可是也不會print 結果,由於匹配結果是空的!,如下正確!!能夠匹配
import re match=re.match(r'[1-9]\d{5}','100081 BIT') if match: print(match.group(0) ans: 100081
(3)re.findall(pattern, string, flags=0) 返回列表類型
import re ls=re.findall(r'[1-9]\d{5}','BIT 100081 XYT 100099') if ls: print(ls) ans: ['100081', '100099']
(4)re.split(pattern, string, maxsplit=0,flags=0) 返回列表類型,maxsplit最大分割數,超過它的做爲一個總體最後再輸出來.
import re print(re.split(r'[0-9]\d{5}','BIT 100081 XYT 100099')) ans: ['BIT ', ' XYT ', '']
上述將匹配的刪除,增長maxsplit=1,結果是:
import re print(re.split(r'[0-9]\d{5}','BIT 100081 XYT 100099',maxsplit=1)) ans: ['BIT ', ' XYT 100099']
(5)re.finditer(pattern, string,flags=0)
import re for m in re.finditer(r'[1-9]\d{5}','BIT 100081 XYT 100099'): if m: print(m.group(0)) ans: 100081 100099
100081
100099
(6)re.sub(pattern,repl, string,count=0,flags=0):替換全部匹配的字符串,並返回替換後的字符串
其中repl:表示替代匹配字符串的字符串(新的字符串)
count 表示匹配最大替換的次數
import re subs=re.sub(r'[1-9]\d{5}',':zipcode','BIT 100081 XYT 100099') print(subs) ans: BIT :zipcode XYT :zipcode
上述六個經常使用的概括:
其中re.search() re.match() 返回match對象, re.finditer()每一個迭代元素是match對象
補充:re 的另外一種等價用法
import re rst1=re.search(r'[1-9]\d{5}','BIT 100081 XYT 100099') print(rst1.group(0)) pat=re.compile(r'[1-9]\d{5}')#先進行編譯, rst2=pat.search('BIT 100089 XYT 100099') print(rst2.group(0)) ans: 100081 100089
#re.compile能夠將正則表達式的字符串形式編譯成正則表達式對象
re.compile(pattern,flags=0)
(三)返回match對象 啥是match對象??
match對象的使用方法::
舉例:
import re m=re.search(r'[1-9]\d{5}','BIT 100081 XYT 100099') print(m.string) print(m.re) print(m.pos) print(m.endpos) print(m.group(0))#只返回一次匹配結果,若要屢次匹配結果則 用re.finditer() print(m.start()) print(m.end()) print(m.span())
結果:
BIT 100081 XYT 100099 re.compile('[1-9]\\d{5}') #說明了只有通過compile的纔是真正的正則表達式 0 21 100081#返回第一個匹配結果 4 #匹配結果在原字符串中的起始位置 10 (4, 10)
(四)re的貪婪匹配
import re #re默認採用貪婪匹配,即輸出匹配最長的子串 m=re.search(r'py.*n','pyanbbncccn') print(m.group()) #如何匹配最短的字符串? 最小匹配,加一個?就能夠了 m1=re.search(r'py.?*n','pyanbbncccn') print(m1.group()) ans: pyanbbncccn pyan