banner
cos

cos

愿热情永存,愿热爱不灭,愿生活无憾
github
tg_channel
bilibili

python網絡爬蟲學習筆記(一) 爬取簡單靜態網頁

一、使用 urllib3 實現 HTTP 請求#

1. 生成請求#

  • 通過 request 方法生成請求,原型如下

urllib3.request(method,url,fields=None,headers=None,**urlopen_kw)

參數說明
method接收 string。表示請求的類型,如 "GET"(通常使用)、"HEAD"、"DELETE" 等,無默認值
url接收 string。表示字符串形式的網址。無默認值
fields接收 dict。表示請求類型所帶的參數。默認為 None
headers接收 dict。表示請求頭所帶參數。默認為 None
**urlopen_kw: 接收 dict 和 python 中的數據類型的數據,依據具體需求及請求的類型可添加的參數,通常參數賦值為字典類型或者具體數據
code:
import urllib3
http = urllib3.PoolManager()
rq = http.request('GET',url='http://www.pythonscraping.com/pages/page3.html')
print('伺服器響應碼:', rq.status)
print('響應實體:', rq.data)

2. 處理請求頭#

傳入 headers 參數可通過定義一個字典類型實現,定義一個包含 User-Agent 信息的字典,使用瀏覽器為火狐和 chrome 瀏覽器,操作系統為 "Window NT 6.1;Win64; x64",向網站 "http://www.tipdm/index.html" 發送帶 headers 參數的 GET 請求,hearders 參數為定義的 User-Agent 字典

import urllib3
http = urllib3.PoolManager()
head = {'User-Agent':'Window NT 6.1;Win64; x64'}
http.request('GET',url='http://www.pythonscraping.com/pages/page3.html',headers=head)

3.Timeout 設置#

為防止因網路不穩定等原因丟包,可在請求中增加 timeout 參數設置,通常為浮點數,可直接在 url 後設置該次請求的全部參數,也可以分別設置這次請求的連接與讀取 timeout 參數,在 PoolManager 實例中設置 timeout 參數可應用至該實例的全部請求中

直接設置

http.request('GET',url='',headers=head,timeout=3.0)
#超過3s的話超時終止
http.request('GET',url='http://www.pythonscraping.com/pages/page3.html',headers=head,timeout=urllib3.Timeout(connect=1.0,read=2.0))
#連接超過1s,讀取超過2s終止

應用至該實例的全部請求中

import urllib3
http = urllib3.PoolManager(timeout=4.0)
head = {'User-Agent':'Window NT 6.1;Win64; x64'}
http.request('GET',url='http://www.pythonscraping.com/pages/page3.html',headers=head)
#超過4s超時

4. 請求重試設置#

urllib3 庫可以通過設置 retries 參數對重試進行控制。默認進行 3 次請求重試,並進行 3 次重定向。自定義重試次數通過賦值一個整型給 retries 參數實現,可通過定義 retries 實例來定制請求重試次數及重定向次數。若需要同時關閉請求重試及重定向則可以將 retries 參數賦值為 False,僅關閉重定向則將 redirect 參數賦值為 False。與 Timeout 設置類似,可以在 PoolManager 實例中設置 retries 參數控制全部該實例下的請求重試策略。

應用至該實例的全部請求中

import urllib3
http = urllib3.PoolManager(timeout=4.0,retries=10)
head = {'User-Agent':'Window NT 6.1;Win64; x64'}
http.request('GET',url='http://www.pythonscraping.com/pages/page3.html',headers=head)
#超過4s超時 重試10次

5. 生成完整 HTTP 請求#

使用 urllib3 庫實現向http://www.pythonscraping.com/pages/page3.html 生成一個完整的請求,該請求應當包含連結、請求頭、超時時間和重試次數設置。
在這裡插入圖片描述
在這裡插入圖片描述
注意編碼方式 utf-8

import urllib3
#發送請求實例
http = urllib3.PoolManager()
#網址
url = 'http://www.pythonscraping.com/pages/page3.html'
#請求頭
head = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.56'}
#超時時間
tm = urllib3.Timeout(connect=1.0,read=3.0)
#重試次數和重定向次數設置,生成請求
rq = http.request('GET',url=url,headers=head,timeout=tm,redirect=4)
print('伺服器響應碼:', rq.status)
print('響應實體:', rq.data.decode('utf-8'))
伺服器響應碼: 200
響應實體: <html>
<head>
<style>
img{
	width:75px;
}
table{
	width:50%;
}
td{
	margin:10px;
	padding:10px;
}
.wrapper{
	width:800px;
}
.excitingNote{
	font-style:italic;
	font-weight:bold;
}
</style>
</head>
<body>
<div id="wrapper">
<img src="../img/gifts/logo.jpg" style="float:left;">
<h1>Totally Normal Gifts</h1>
<div id="content">Here is a collection of totally normal, totally reasonable gifts that your friends are sure to love! Our collection is
hand-curated by well-paid, free-range Tibetan monks.<p>
We haven't figured out how to make online shopping carts yet, but you can send us a check to:<br>
123 Main St.<br>
Abuja, Nigeria
</br>We will then send your totally amazing gift, pronto! Please include an extra $5.00 for gift wrapping.</div>
<table id="giftList">
<tr><th>
Item Title
</th><th>
Description
</th><th>
Cost
</th><th>
Image
</th></tr>

<tr id="gift1" class="gift"><td>
Vegetable Basket
</td><td>
This vegetable basket is the perfect gift for your health conscious (or overweight) friends!
<span class="excitingNote">Now with super-colorful bell peppers!</span>
</td><td>
$15.00
</td><td>
<img src="../img/gifts/img1.jpg">
</td></tr>

<tr id="gift2" class="gift"><td>
Russian Nesting Dolls
</td><td>
Hand-painted by trained monkeys, these exquisite dolls are priceless! And by "priceless," we mean "extremely expensive"! <span class="excitingNote">8 entire dolls per set! Octuple the presents!</span>
</td><td>
$10,000.52
</td><td>
<img src="../img/gifts/img2.jpg">
</td></tr>

<tr id="gift3" class="gift"><td>
Fish Painting
</td><td>
If something seems fishy about this painting, it's because it's a fish! <span class="excitingNote">Also hand-painted by trained monkeys!</span>
</td><td>
$10,005.00
</td><td>
<img src="../img/gifts/img3.jpg">
</td></tr>

<tr id="gift4" class="gift"><td>
Dead Parrot
</td><td>
This is an ex-parrot! <span class="excitingNote">Or maybe he's only resting?</span>
</td><td>
$0.50
</td><td>
<img src="../img/gifts/img4.jpg">
</td></tr>

<tr id="gift5" class="gift"><td>
Mystery Box
</td><td>
If you love suprises, this mystery box is for you! Do not place on light-colored surfaces. May cause oil staining. <span class="excitingNote">Keep your friends guessing!</span>
</td><td>
$1.50
</td><td>
<img src="../img/gifts/img6.jpg">
</td></tr>
</table>
</p>
<div id="footer">
&copy; Totally Normal Gifts, Inc. <br>
+234 (617) 863-0736
</div>

</div>
</body>
</html>

二、使用 requests 庫實現 HTTP 請求#

import requests
url = 'http://www.pythonscraping.com/pages/page3.html'
rq2 = requests.get(url)
rq2.encoding = 'utf-8'
print('響應碼:',rq2.status_code)
print('編碼:',rq2.encoding)
print('請求頭:',rq2.headers)
print('實體:',rq2.text)

解決字符編碼問題#

需要注意的是,當 requests 庫猜測錯時,需要手動指定 encoding 編碼,避免返回的網頁內容解析出現亂碼。手動指定的方法並不靈活,無法自適應對應爬取過程中不同網頁的編碼,而使用 chardet 庫比較簡便靈活,chardet 庫是一個非常優秀的字符串∕文件編碼檢測模塊。
chardet 庫使用 detect 方法檢測給定字符串的編碼,detect 方法常用的參數及其說明如下

參數說明
byte_str接收 string。表示需要檢測編碼的字符串。無默認值
import chardet
chardet.detect(rq2.content)

輸出:100% 的概率是用 ascii 碼編碼的
在這裡插入圖片描述
完整代碼

import requests
import chardet
url = 'http://www.pythonscraping.com/pages/page3.html'
head={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Edg/88.0.705.56'}
rq2 = requests.get(url,headers=head,timeout=2.0)
rq2.encoding = chardet.detect(rq2.content)['encoding']
print('實體:',rq2.content)

三、解析網頁#

chrome 開發者工具各面板功能如下
在這裡插入圖片描述

1. 元素面板#

在爬蟲開發中,元素面板主要用來查看頁面元素所對應的位置,比如圖片所在位置或文字連結所對應的位置。面板左側可看到當前頁面的結構,為樹狀結構,單擊三角符號即可展開分支。
在這裡插入圖片描述

2. 源代碼面板#

切換至源代碼面板(Sources)單擊左側 “tipdm” 文件夾中的 “index.html” 文件,將在中間顯示其包含的完整代碼,如圖所示。

3. 網路面板#

切換至網路面板(Network),需先重新加載頁面,點擊某資源,將在中間顯示該資源的頭部信息、預覽、響應信息、Cookies 和花費時間詳情,如圖所示。
在這裡插入圖片描述

四、使用正則表達式解析網頁#

1. Python 正則表達式:尋找字符串中的姓名和電話號碼#

正則表達式是一種可以用於模式匹配和替換的工具,可以讓用戶通過使用一系列的特殊字符構建匹配模式,然後把匹配模式與待比較字符串或文件進行比較,根據比較對象中是否包含匹配模式,執行相應的程序。

rawdata = “555-1239Moe Szyslak(636) 555-0113Burns, C.Montgomery555-6542Rev. Timothy Lovejoy555 8904Ned Flanders636-555-3226Simpson,Homer5553642Dr. Julius Hibbert ”

試試

import re
string = '1. A small sentence - 2.Anthoer tiny sentence. '
print('re.findall:',re.findall('sentence',string))
print('re.search:',re.search('sentence',string))
print('re.match:',re.match('sentence',string))
print('re.match:',re.match('1. A small sentence',string))
print('re.sub:',re.sub('small','large',string)) 
print('re.sub:',re.sub('small','',string)) 

輸出:
re.findall: ['sentence', 'sentence']
re.search: <re.Match object; span=(11, 19), match='sentence'>
re.match: None
re.match: <re.Match object; span=(0, 19), match='1. A small sentence'>
re.sub: 1. A large sentence - 2.Anthoer tiny sentence.
re.sub: 1. A sentence - 2.Anthoer tiny sentence.

常用廣義化符號
1、英文句號 “.”:能代表除換行符 “\n” 任意一個字符;

string = '1. A small sentence - 2.Anthoer tiny sentence. '
re.findall('A.',string)

輸出:['A ', 'An']

2、字符類 “[]”:被包含在中括號內部,任何中括號內的字符都會被匹配;

string = 'small smell smll smsmll sm3ll sm.ll sm?ll sm\nll sm\tll'
print('re.findall:',re.findall('sm.ll',string))
print('re.findall:',re.findall('sm[asdfg]ll',string))
print('re.findall:',re.findall('sm[a-zA-Z0-9]ll',string))
print('re.findall:',re.findall('sm\.ll',string))
print('re.findall:',re.findall('sm[.?]ll',string))

輸出:

re.findall: ['small', 'smell', 'sm3ll', 'sm.ll', 'sm?ll', 'sm\tll']
re.findall: ['small']
re.findall: ['small', 'smell', 'sm3ll']
re.findall: ['sm.ll']
re.findall: ['sm.ll', 'sm?ll']

3. 量化符號 "{}": 可以被匹配多少次

print('re.findall:',re.findall('sm..ll',string))
print('re.findall:',re.findall('sm.{2}ll',string))
print('re.findall:',re.findall('sm.{1,2}ll',string))
print('re.findall:',re.findall('sm.{1,}ll',string))
print('re.findall:',re.findall('sm.?ll',string)) # {0,1}
print('re.findall:',re.findall('sm.+ll',string)) # {0,}
print('re.findall:',re.findall('sm.*ll',string)) # {1,}

輸出:
re.findall: ['smsmll']
re.findall: ['smsmll']
re.findall: ['small', 'smell', 'smsmll', 'sm3ll', 'sm.ll', 'sm?ll']
re.findall: ['small smell smll smsmll sm3ll sm.ll sm?ll', 'sm\tll']
re.findall: ['small', 'smell', 'smll', 'smll', 'sm3ll', 'sm.ll', 'sm?ll', 'sm\tll']
re.findall: ['small smell smll smsmll sm3ll sm.ll sm?ll', 'sm\tll']
re.findall: ['small smell smll smsmll sm3ll sm.ll sm?ll', 'sm\tll']

ps:貪婪規則,儘可能匹配多的

完整代碼#

import pandas as pd
rawdata = '555-1239Moe Szyslak(636) 555-0113Burns, C.Montgomery555-6542Rev. Timothy Lovejoy555 8904Ned Flanders636-555-3226Simpson,Homer5553642Dr. Julius Hibbert'
names = re.findall('[A-Z][A-Za-z,. ]*',rawdata)
print(names)
number = re.findall('\(?[0-9]{0,3}\)?[ \-]?[0-9]{3}[ \-]?[0-9]{4}',rawdata)
print(number)
pd.DataFrame({'Name':names,'TelPhone':number})

輸出:
在這裡插入圖片描述

五、使用 Xpath 解析網頁#

XML 路徑語言(XML Path Language),它是一種基於 XML 的樹狀結構,在數據結構樹中找尋節點,確定 XML 文檔中某部分位置的語言。使用 Xpath 需要從 lxml 庫中導入 etree 模塊,還需使用 HTML 類對需要匹配的 HTML 對象進行初始化(XPath 只能處理文檔的 DOM 表現形式)。HTML 類的基本語法格式如下。

1. 基本語法#

lxml.etree.HTML(text, parser=None, *, base_url=None)

參數說明
text接收 str。表示需要轉換為 HTML 的字符串。無默認值
parser接收 str。表示選擇的 HTML 解析器。無默認值
base_url接收 str。表示設置文檔的原始 URL,用於在查找外部實體的相對路徑。默認為 None
若 HTML 中的節點沒有閉合,etree 模塊也提供自動補全功能。調用 tostring 方法即可輸出修正後的 HTML 代碼,但是結果為 bytes 類型,需要使用 decode 方法轉成 str 類型。

Xpath 使用類似正則的表達式來匹配 HTML 文件中的內容,常用匹配表達式如下。

表達式說明
nodename選取 nodename 節點的所有子節點
/從當前節點選取直接子節點
//從當前節點選取子孫節點
.選取當前節點
..選取當前節點的父節點
@選取屬性

2. 謂語#

Xpath 中的謂語用來查找某個特定的節點或包含某個指定的值的節點,謂語被嵌在路徑後的方括號中,如下。

表達式說明
/html/body/div[1]選取屬於 body 子節點下的第一個 div 節點
/html/body/div[last()]選取屬於 body 子節點下的最後一個 div 節點
/html/body/div[last()-1]選取屬於 body 子節點下的倒數第二個 div 節點
/html/body/div[positon()<3]選取屬於 body 子節點下的下前兩個 div 節點
/html/body/div[@id]選取屬於 body 子節點下的帶有 id 屬性的 div 節點
/html/body/div[@id="content"]選取屬於 body 子節點下的 id 屬性值為 content 的 div 節點
/html/body/div[xx>10.00]選取屬於 body 子節點下的 xx 元素值大於 10 的節點

3. 功能函數#

Xpath 中還提供部分功能函數進行模糊搜索,有時對象僅掌握了其部分特徵,當需要模糊搜索該類對象時,可使用功能函數來實現,具體函數如下。

功能函數示例說明
starts-with//div[starts-with(@id,”co”)]選取 id 值以 co 開頭的 div 節點
contains//div[contains(@id,”co”)]選取 id 值包含 co 的 div 節點
and//div[contains(@id,”co”)andcontains(@id,”en”)]選取 id 值包含 co 和 en 的 div 節點
text()//li[contains(text(),”first”)]選取節點文本包含 first 的 div 節點

4. 谷歌開發者工具使用#

谷歌開發者工具提供非常便捷的複製 xpath 路徑的方法
在這裡插入圖片描述
eg:爬取知乎熱榜完整代碼
試了一下爬取知乎熱榜,需要登錄所以可以自己登錄然後獲取 cookie

import requests
from lxml import etree
url = "https://www.zhihu.com/hot"
hd = { 'Cookie':'你的Cookie', #'Host':'www.zhihu.com',
        'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}

response = requests.get(url, headers=hd)
html_str = response.content.decode()
html = etree.HTML(html_str)
title = html.xpath("//section[@class='HotItem']/div[@class='HotItem-content']/a/@title")
href = html.xpath("//section[@class='HotItem']/div[@class='HotItem-content']/a/@href")
f = open("zhihu.txt",'r+')
for i in range(1,41):
    print(i,'.'+title[i])
    print('連結:'+href[i])
    print('-'*50)
    f.write(str(i)+'.'+title[i]+'\n')
    f.write('連結:'+href[i]+'\n')
    f.write('-'*50+'\n')
f.close()

爬取結果
在這裡插入圖片描述

六、數據存儲#

1. 以 json 格式存儲#

import requests
from lxml import etree
import json
#上面代碼略
with open('zhihu.json','w') as j:
    json.dump({'title':title,'hrefL':href},j,ensure_ascii=False)

存儲結果(ps: 經過文件格式化處理)
在這裡插入圖片描述

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。