在開始之前,請確保你的電腦上已經安裝好了BeautifulSoup庫,可以通過在命令行中輸入pip install beautifulsoup4來進行安裝。
一、數據解析
在爬取之前,我們需要檢測下響應狀態碼是否為200,如果請求失敗,我們將爬取不到任何數據:
import requests
re = requests.get('https://book.douban.com/top250')
if re.status_code == 200:
print('請求成功!')
else:
print('請求失敗!響應狀態碼為{}'.format(re.status_code))
響應狀態碼為418,我們請求失敗了,這是為什么呢?因為豆瓣有反爬蟲機制,我們無法通過直接請求服務器來爬取數據,必須在發起請求之前將自己偽裝起來。
1.1 反爬蟲
反爬蟲是網站限制爬蟲的一種策略。它并不是禁止爬蟲(完全禁止爬蟲幾乎不可能,也可能誤傷正常用戶),而是限制爬蟲,讓爬蟲在網站可接受的范圍內爬取數據,不至于導致網站癱瘓無法運行。常見的反爬蟲方式有判別身份和IP限制兩種,這里我們先介紹前者,后者稍后再提及。
有些網站在識別出爬蟲后,會拒絕爬蟲進行訪問,比如之前提到的豆瓣。那我們怎樣做才能不被識別出來呢?在此之前,我們先嘗試一下直接爬取:
import requests
re = requests.get('https://book.douban.com/top250')
print(re.text)
結果是什么都沒有輸出,因為豆瓣將我們的爬蟲識別了出來并拒絕提供內容。你可能會有疑問,爬蟲不是模擬瀏覽器訪問網站、獲取網頁源代碼的嗎?為什么就被識別出來了呢?事實上,無論是瀏覽器還是爬蟲,訪問網站時都會帶上一些信息用于身份識別,而這些信息都被存儲在一個叫請求頭(request headers)的地方。
服務器會通過請求頭里的信息來判別訪問者的身份。請求頭里的字段有很多,我們暫時只需了解user-agent(用戶代理)即可。user-agent里包含了操作系統、瀏覽器類型、版本等信息,通過修改它我們就能成功地偽裝成瀏覽器并爬取我們想要的數據。
那么如何找到user-agent呢?操作步驟如下:
- 首先按F12(或Fn+F12),然后單擊上方的Network標簽。
- 此時打開https://book.douban.com/top250,在Name一列中找到top250并單擊。
- 在右邊的Headers中找到Request Headers,User-Agent就在其中。
選中后將其復制下來,我的瀏覽器的User-Agent是Mozilla/5.0 (windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/84.0.4147.125 Safari/537.36,而requests默認的User-Agent是Python-requests/2.24.0。默認的User-Agent和在頭上貼著“我是爬蟲”的紙條沒有什么區別,很容易被服務器識別出來。因此我們需要修改請求頭里的user-agent字段內容,將爬蟲偽裝成瀏覽器。
我們只需定義一個字典(請求頭字段作為鍵,字段內容作為值)傳遞給headers參數即可,方法如下:
import requests
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get('https://book.douban.com/top250', headers=headers)
print(re.text)
這樣就能正常輸出內容了(結果太長這里不展示)。除了user-agent之外的其他請求頭字段也能以同樣的方式添加進去,但大部分情況下我們只需要添加user-agent字段即可。當我們加了user-agent字段還是無法獲取到數據時,說明該網站還通過別的信息來驗證身份,我們可以將請求頭里的字段都添加進去再嘗試。
1.2 BeautifulSoup 對象
接下來我們將網頁源代碼解析成BeautifulSoup對象:
import requests
from bs4 import BeautifulSoup
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get('https://book.douban.com/top250', headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
需要注意的是,創建BeautifulSoup對象時需要傳入兩個參數,第一個參數是要解析的HTML文本,即網站源代碼的字符串形式re.text。第二個參數是解析HTML的解析器,html.parser是Python中內置的解析器,較為簡單方便。
接下來我們分別打印soup和re.text,觀察其內容有無區別:
import requests
from bs4 import BeautifulSoup
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get('https://book.douban.com/top250', headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
print(soup)
print(re.text)
仔細觀察后會發現兩次打印的內容完全一樣!既然都一樣,我們何苦費這么大力將網頁源代碼解析成BeautifulSoup對象呢?為什么不直接打印re.text呢?
其實,它們只是看上去一樣,實際上卻屬于不同種類。現在嘗試用type()函數將re.text和BeautifulSoup對象的類型打印出來對比一下:
import requests
from bs4 import BeautifulSoup
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get('https://book.douban.com/top250', headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
print(type(re.text))
soup = BeautifulSoup(re.text, 'html.parser')
print(type(soup))
通過輸出結果可以看出,re.text的類型是字符串,而soup的類型是BeautifulSoup對象,它倆是完全不同的東西。相比字符串,BeautifulSoup對象里有很多強大的方法和屬性。通過這些方法和屬性,我們就能方便快捷地提取出我們所需要的數據。
二、數據提取
BeautifulSoup對象里的方法和屬性有很多,我們只學習其中最常用的一些,這些足以應付大多數場景。等你真正的入門后,可以自行學習那些更高階的知識去解決更復雜的問題。
2.1 find()與find_all()
BeautifulSoup對象里的find()和find_all()是我們提取數據最常用的兩個方法。借助它們,我們可以過濾掉HTML頁面里的無用數據,輕松地找到我們需要的數據。
我們先來看一下find()和find_all()的作用和區別:
方法作用find()返回符合條件的首個數據find_all()返回符合條件的所有數據
我們通過一個例子進一步熟悉這兩個方法,假設我們獲取到的網頁源代碼如下:
<div class="content">
<a href="https://douban.com">登錄/注冊</a>
<h1>豆瓣讀書 Top 250</h1>
<div class="article">
<a href="https://market.douban.com/book/?utm_campaign=book_nav_freyr&utm_source=douban&utm_medium=pc_web">豆瓣書店</a>
<div class="item">
<a href="https://book.douban.com/subject/1007305/">紅樓夢</a>
</div>
<div class="item">
<a href="https://book.douban.com/subject/6082808/">百年孤獨</a>
</div>
<div class="item">
<a href="https://book.douban.com/subject/10554308/">白夜行</a>
</div>
</div>
</div>
接下來分別使用find()與find_all(),觀察輸出結果的差異:
print(soup.find('a'))
print(soup.find_all('a'))
其中,使用find()方法輸出的結果為:
<a href="https://douban.com">登錄/注冊</a>
而使用find_all()方法輸出的結果為:
[
<a href="https://douban.com">登錄/注冊</a>,
<a href="https://market.douban.com/book/?utm_campaign=book_nav_freyr&utm_source=douban&utm_medium=pc_web">豆瓣書店</a>,
<a href="https://book.douban.com/subject/1007305/">紅樓夢</a>,
<a href="https://book.douban.com/subject/6082808/">百年孤獨</a>,
<a href="https://book.douban.com/subject/10554308/">白夜行</a>
]
可以看到,find()方法返回了第一個a標簽,而find_all()方法則返回了所有的a標簽。它倆的用法基本一樣,都是傳入HTML標簽名稱,返回符合該HTML標簽的數據。區別是find()方法只返回第一個符合條件的標簽,而find_all()方法返回所有符合條件的標簽列表。
除了傳入標簽名稱外,這兩個方法還支持傳入屬性進行篩選,返回符合條件的數據。例如:
soup.find('div', id='a') # 查找id='a'的div標簽
soup.find_all('var', class_='b') # 查找所有class='b'的var標簽
soup.find('button', id='c', class_='d') # 查找id='c'且class='d'的button標簽
注:因為class是Python中定義類的關鍵字,因此用class_表示HTML中的class。
2.2 Tag對象
BeautifulSoup將HTML中的元素封裝成了Tag對象。和BeautifulSoup對象一樣,Tag對象里也有find()和find_all()方法。因此,我們可以不斷地調用這兩個方法,一層一層地找到我們需要的數據。依然使用之前的例子:
<div class="content">
<a href="https://douban.com">登錄/注冊</a>
<h1>豆瓣讀書 Top 250</h1>
<div class="article">
<a href="https://market.douban.com/book/?utm_campaign=book_nav_freyr&utm_source=douban&utm_medium=pc_web">豆瓣書店</a>
<div class="item">
<a href="https://book.douban.com/subject/1007305/">紅樓夢</a>
</div>
<div class="item">
<a href="https://book.douban.com/subject/6082808/">百年孤獨</a>
</div>
<div class="item">
<a href="https://book.douban.com/subject/10554308/">白夜行</a>
</div>
</div>
</div>
我們可以看到,書名在a標簽中。但如果直接使用soup.find_all(‘a’)的話,第二行的“登錄/注冊”和第五行的“豆瓣書店”也會被獲取到,因此我們需要將這些無效數據過濾掉。
深入思考一下不難發現,書名在class="item"的div標簽里的a標簽內。我們只要先找到所有class="item"的div標簽,然后再找到其中的a標簽即可,因此我們可以像下面這樣來獲取書名的數據:
items = soup.find_all('div', class_='item')
for item in items:
print(item.find('a'))
輸出結果:
<a href="https://book.douban.com/subject/1007305/">紅樓夢</a>
<a href="https://book.douban.com/subject/6082808/">百年孤獨</a>
<a href="https://book.douban.com/subject/10554308/">白夜行</a>
這樣,我們就找到了所有書名的數據。但此時返回的還是Tag對象。如果我們只想要書名和對應的鏈接呢?這就用到了Tag對象的text屬性和HTML屬性名取值。
items = soup.find_all('div', class_='item')
for item in items:
tag = item.find('a')
name = tag.text
link = tag['href']
print(name, link)
輸出結果:
紅樓夢 https://book.douban.com/subject/1007305/
百年孤獨 https://book.douban.com/subject/6082808/
白夜行 https://book.douban.com/subject/10554308/
我們通過Tag對象的text屬性拿到了a標簽里的文字內容,即紅樓夢等。然后我們通過和字典取值一樣的方式,將HTML屬性名作為鍵,得到了對應屬性的值。這里是以href屬性為例,其他的HTML屬性也同樣可以。
Tag對象的常用屬性和方法總結如下:
屬性/方法作用tag.find()返回符合條件的首個數據tag.find_all()返回符合條件的所有數據tag.text獲取標簽的文本內容tag[‘屬性名’]獲取標簽屬性的值
我們通過多次調用find()或find_all()方法一層層地找到了我們需要的數據。那有沒有什么方法可以直接就找到我們需要的數據,而不用多次查找嗎?
答案是肯定的,這就需要用到css選擇器了。
2.3 CSS選擇器
在CSS選擇器中,#代表id,.代表class。比如:#a表示id=‘a’的所有元素,.b表示class=‘b’的所有元素。當然,我們也可以直接通過標簽名選擇對應的元素,例如:a表示所有的a元素。
事實上,它們還可以組合在一起,選擇同時符合條件的元素,比如:a#b表示所有id=‘b’的a元素,d.c 表示所有class=‘c’的d元素,#b.c 表示所有 id=‘b’ 且 class=‘c’ 的元素,.c.f 表示所有class同時為c和f的元素。
需要注意的是,選擇同時符合條件的元素,選擇器之間不能有空格,如果寫成.c .f就是另一個意思了。這是因為,當兩個選擇器之間加了空格,表示子元素選擇。還是以.c .f為例,它表示選擇所有class=‘c’的元素里面class=‘f’的元素,即嵌套在class=‘c’的元素里面class=‘f’的元素。這個嵌套可以是任意層級的,只要在里面就行,不要求直接嵌套在第一層。如果只需要直接嵌套在第一層符合條件的元素,可以用>分隔,例如:.c > .f。
我們來通過一個例子了解一下CSS選擇器的用法:
from bs4 import BeautifulSoup
html = '''
<div class="item">
<p class="book">紅樓夢</p>
<div class="hot">
<p class="book">白夜行</p>
</div>
</div>'''
soup = BeautifulSoup(html, 'html.parser')
print(soup.select('.item.book'))
print(soup.select('.item .book'))
print(soup.select('.item > .book'))
輸出結果:
[]
[<p class="book">紅樓夢</p>, <p class="book">白夜行</p>]
[<p class="book">紅樓夢</p>]
BeautifulSoup對象有一個select()方法,我們將CSS 選擇器傳進去即可直接找到我們需要的元素。之前查找在class="item"的div標簽里的a標簽的代碼就可以這樣寫:
items = soup.select('div.item a')
for item in items:
name = item.text
link = item['href']
print(name, link)
可以看到,我們一次性就將所有符合條件的a元素找了出來,同樣的功能,代碼變得更加簡潔了。
三、單個網頁爬取
學習到這里,現在你應該可以獨自完成爬取豆瓣圖書的任務了,豆瓣圖書Top250地址:https://book.douban.com/top250。
接下來我會給出具體的思路,不過建議你先嘗試獨立完成這個任務。
首先,此前我們已經提到過,豆瓣是禁止反爬蟲的,我們通過修改User-Agent偽裝成瀏覽器成功“騙過”了豆瓣的識別:
import requests
from bs4 import BeautifulSoup
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get('https://book.douban.com/top250', headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
接下來,我們需要利用CSS選擇器將我們所需要的數據存儲在items變量中,但是,如何找到圖書名稱所在的標簽呢?
其實很簡單,我們先打開https://book.douban.com/top250,右鍵單擊紅樓夢>檢查,如下圖所示:

不難看出,書名是a標簽內屬性title的值,且這個a標簽位于class=“pl2”的div標簽內。
在知道了書名的“坐標”后,我們就可以使用CSS選擇器啦:
items = soup.select('div.pl2 a')
1
此時items變量實際上是一個由Tag對象組成的列表,我們可以通過循環打印書名和對應的鏈接:
for item in items:
name = item['title']
link = item['href']
print(name, link)
1234
完整的代碼如下:
import requests
from bs4 import BeautifulSoup
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get('https://book.douban.com/top250', headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
items = soup.select('div.pl2 a')
for item in items:
name = item['title']
link = item['href']
print(name, link)
1234567891011121314
爬取到的結果:
紅樓夢 https://book.douban.com/subject/1007305/
活著 https://book.douban.com/subject/4913064/
1984 https://book.douban.com/subject/4820710/
百年孤獨 https://book.douban.com/subject/6082808/
飄 https://book.douban.com/subject/1068920/
三體全集 https://book.douban.com/subject/6518605/
三國演義(全二冊) https://book.douban.com/subject/1019568/
白夜行 https://book.douban.com/subject/10554308/
房思琪的初戀樂園 https://book.douban.com/subject/27614904/
福爾摩斯探案全集(上中下) https://book.douban.com/subject/1040211/
動物農場 https://book.douban.com/subject/2035179/
小王子 https://book.douban.com/subject/1084336/
天龍八部 https://book.douban.com/subject/1255625/
撒哈拉的故事 https://book.douban.com/subject/1060068/
安徒生童話故事集 https://book.douban.com/subject/1046209/
哈利•波特 https://book.douban.com/subject/24531956/
人類簡史 https://book.douban.com/subject/25985021/
沉默的大多數 https://book.douban.com/subject/1054685/
圍城 https://book.douban.com/subject/1008145/
平凡的世界(全三部) https://book.douban.com/subject/1200840/
殺死一只知更鳥 https://book.douban.com/subject/6781808/
局外人 https://book.douban.com/subject/4908885/
明朝那些事兒(1-9) https://book.douban.com/subject/3674537/
霍亂時期的愛情 https://book.douban.com/subject/10594787/
笑傲江湖(全四冊) https://book.douban.com/subject/1002299/
12345678910111213141516171819202122232425
仔細觀察不難發現,上述所說的a標簽內的文本內容只有書名,因此除了使用item[‘title’]外,我們還可以使用item.text:
for item in items:
name = item.text
link = item['href']
print(name, link)
1234
輸出結果(結果太長這里僅展示前面的一部分):
紅樓夢
https://book.douban.com/subject/1007305/
活著
https://book.douban.com/subject/4913064/
1984
https://book.douban.com/subject/4820710/
百年孤獨
https://book.douban.com/subject/6082808/
1234567891011121314151617181920
為什么使用item.text就會出現一些奇怪的換行呢?
我們知道,item.text是獲取標簽內的所有文本內容,空格,換行符等都會獲取,我們再來看一下之前的a標簽:

顯然a標簽里存在空格和換行符,因此如果想使用item.text輸出結果,我們必須使用join()方法去掉這些多余的空格和換行符:
for item in items:
name = ''.join(item.text.split())
link = item['href']
print(name, link)
1234
這樣結果就可以正常顯示啦。
四、爬取所有Top250圖書
可能你已經發現了,我們之前爬取的圖書僅僅是第一頁的,并不是所有的Top250圖書,那么如何爬取所有的呢?
我們進入第二頁后,可以看到網址變成了https://book.douban.com/top250?start=25,相比原來多了個?start=25。
我們再進入第一頁,會發現網址變成了https://book.douban.com/top250?start=0。之后,再進入最后一頁,網址變成了https://book.douban.com/top250?start=225。
我想你應該已經找到了其中的規律。
很顯然,"start="后面的數字總是以步長25進行遞增,第一頁此數字是0,第十頁此數字是225,這讓我們聯想到了循環。
我們先把之前爬取圖書的代碼封裝成一個函數:
def spider(url):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get(url, headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
items = soup.select('div.pl2 a')
for item in items:
name = item['title']
link = item['href']
print(name, link)
1234567891011
豆瓣圖書Top250共有十頁,我們根據之前找出來的規律,利用循環生成這十個網頁地址:
for i in range(0, 250, 25):
douban_book = 'https://book.douban.com/top250?start=%s' % str(i)
12
之后,將它們組裝在一起,我們就可以爬取所有圖書了:
import requests
from bs4 import BeautifulSoup
def spider(url):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get(url, headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
items = soup.select('div.pl2 a')
for item in items:
name = item['title']
link = item['href']
print(name, link)
for i in range(0, 250, 25):
douban_book = 'https://book.douban.com/top250?start=%s' % str(i)
spider(douban_book)
1234567891011121314151617181920
但考慮到輸出結果太長,不方便在終端查看,我們可以將爬取到的結果寫入文件:
import requests
from bs4 import BeautifulSoup
def spider(url, filename):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get(url, headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
items = soup.select('div.pl2 a')
with open(filename, 'a', encoding=re.encoding) as f:
for item in items:
line = item['title'] + " " + item['href'] + "n"
f.write(line)
for i in range(0, 250, 25):
douban_book = 'https://book.douban.com/top250?start=%s' % str(i)
spider(douban_book, 'doubanTop250.txt')
1234567891011121314151617181920
效果:

五、防BAN策略
爬蟲在網頁上爬取數據時如果不加任何限制會“高速”訪問對方的服務器,如果訪問太快容易導致被對方服務器封禁,因為正常人是不會在1秒內訪問幾十次甚至上百次網站的,這樣就會導致我們在一段時間內無法訪問這個網站。所以,如果你訪問過于頻繁,即使通過修改User-Agent偽裝成瀏覽器,也還是會被識別出爬蟲,并限制你的IP訪問該網站。
那么如何防止自己被封禁呢?這里介紹兩種策略,一種是降低自己的爬取速度,另一種是IP代理。
5.1 使用time.sleep()降低爬取速度
time.sleep(secs)函數推遲調用線程的運行,可通過參數secs(秒數)來進行設置。
我們先來看一個例子:
import time
for i in range(0, 10):
print(i)
time.sleep(1)
12345
運行后,終端先是輸出0,之后每隔1秒輸出一個數字。倘若不加上time.sleep(1),那么這10個數字會在“一瞬間”打印出來。顯然,time.sleep()延遲了打印這個操作。
利用這個特點,我們可以在之前爬取豆瓣圖書Top250的代碼中使用time.sleep(),以降低爬取速度,防止被封:
import requests
from bs4 import BeautifulSoup
from time import sleep as pause
def spider(url, filename):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get(url, headers=headers)
soup = BeautifulSoup(re.text, 'html.parser')
items = soup.select('div.pl2 a')
with open(filename, 'a', encoding=re.encoding) as f:
for item in items:
line = item['title'] + " " + item['href'] + "n"
f.write(line)
for i in range(0, 250, 25):
douban_book = 'https://book.douban.com/top250?start=%s' % str(i)
spider(douban_book, 'doubanTop250.txt')
pause(1)
12345678910111213141516171819202122
這樣我們就可以每隔1秒爬取一頁,降低了訪問頻率。
5.2 IP代理
除了降低訪問頻率之外,我們也可以使用代理來解決IP限制的問題。代理的意思是通過別的IP訪問網站。這樣,在IP被封后我們可以換一個IP繼續爬取數據,或者每次爬取數據時都換不同的IP,避免同一個IP訪問的頻率過高,這樣就能快速地大規模爬取數據了。
如何使用代理呢?請看下例:
import requests
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)
12345678
和headers一樣,我們同樣需要定義一個字典,但傳遞的是proxies參數。我們需要將http和https這兩種協議作為鍵,對應的IP代理作為值,最后將整個字典作為proxies參數傳遞給requests.get()方法即可。
注:IP代理有免費的和收費的,你可以自行在網上尋找。
在爬取大量數據時我們需要很多的IP用于切換。因此,我們需要建立一個IP 代理池(字典列表),每次從中隨機選擇一個傳給proxies參數。
因此,使用IP代理并結合time.sleep()爬取豆瓣圖書Top250再將其寫入文件的完整代碼如下:
import requests
from random import choice
from bs4 import BeautifulSoup as BeS
from time import sleep as pause
def spider(url, filename, proxies):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
}
re = requests.get(url, proxies=proxies, headers=headers)
soup = BeS(re.text, 'html.parser')
items = soup.select('div.pl2 a')
with open(filename, 'a', encoding=re.encoding) as f:
for item in items:
f.write(item['title'] + " " + item['href'] + "n")
filename = 'doubanTop250.txt'
pages = []
proxies_list = []
for i in range(0, 250, 25):
ip_1 = "http://10.10.1.1%s:3128" % str(i // 25)
ip_2 = "http://10.10.1.1%s:1080" % str(i // 25)
douban_book = 'https://book.douban.com/top250?start=%s' % str(i)
prox = {
"http": ip_1,
"https": ip_2,
}
pages.append(douban_book)
proxies_list.append(prox)
for page in pages:
proxies = choice(proxies_list)
spider(page, filename, proxies)
pause(1)
12345678910111213141516171819202122232425262728293031323334353637
上述代碼的IP代理池中的IP代理不可用(IP地址是瞎寫的),所以代碼不會成功運行,這里僅僅是為了展示一個完整的結構。