這篇文章,我們繼續利用 requests 和 xpath 爬取豆瓣電影的短評,下面還是先貼上效果圖:
1、網頁分析
(1)翻頁
我們還是使用 Chrome 瀏覽器打開豆瓣電影中某一部電影的評論進行分析,這里示例為《一出好戲》
和之前一樣,我們可以通過構造 URL 獲取全部網頁的內容,但是這次我們嘗試使用一種新的方法 —— 翻頁
使用快捷鍵 Ctrl+Shift+I
打開開發者工具,然后使用快捷鍵 Ctrl+Shift+C
打開元素選擇工具
此時用鼠標點擊網頁中的 后頁
,就會在源代碼中自動定位到相應的位置
接下來我們用 xpath 匹配下一頁的鏈接地址:
html.xpath('//div[@id="paginator"]/a[@class="next"]/@href')
這樣一來,我們只要在每一頁中通過循環不斷獲取下一頁的內容即可
核心代碼如下:
# 獲取網頁源代碼
def get_page(url):
# 構造請求頭部
headers = {
'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
# 發送請求,獲得響應
response = requests.get(url=url,headers=headers)
# 獲得網頁源代碼
html = response.text
# 返回網頁源代碼
return html
# 解析網頁源代碼,獲取下一頁鏈接
def parse4link(html,base_url):
# 初始化返回結果
link = None
# 構造 _Element 對象
html_elem = etree.HTML(html)
# 匹配下一頁的鏈接地址,注意,它是一個相對地址
url = html_elem.xpath('//div[@id="paginator"]/a[@class="next"]/@href')
# 若匹配成功,則將匹配結果與初始 URL 拼接,構成完整的鏈接地址
if url:
link = base_url + url[0]
return link
(2)分析網頁內容
這一次我們需要的數據包括(這里還是使用 xpath 進行匹配):
- 贊同人數:
//div[@class="comment-item"]/div[2]/h3/span[1]/span/text()
- 評論者:
//div[@class="comment-item"]/div[2]/h3/span[2]/a/text()
- 評價:
//div[@class="comment-item"]/div[2]/h3/span[2]/span[2]/@title
- 評論內容:
//div[@class="comment-item"]/div[2]/p/span/text()
核心代碼如下:
# 解析網頁源代碼,獲取數據
def parse4data(html):
# 構造 _Element 對象
html = etree.HTML(html)
# 贊同人數
agrees = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[1]/span/text()')
# 評論作者
authods = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[2]/a/text()')
# 評價
stars = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[2]/span[2]/@title')
# 評論內容
contents = html.xpath('//div[@class="comment-item"]/div[2]/p/span/text()')
# 獲得結果
data = zip(agrees,authods,stars,contents)
# 返回結果
return data
(3)保存數據
下面將數據分別保存為 txt 文件、json 文件和 csv 文件
import json
import csv
# 打開文件
def openfile(fm):
fd = None
if fm == 'txt':
fd = open('douban_comment.txt','w',encoding='utf-8')
elif fm == 'json':
fd = open('douban_comment.json','w',encoding='utf-8')
elif fm == 'csv':
fd = open('douban_comment.csv','w',encoding='utf-8',newline='')
return fd
# 將數據保存到文件
def save2file(fm,fd,data):
if fm == 'txt':
for item in data:
fd.write('----------------------------------------\n')
fd.write('agree:' + str(item[0]) + '\n')
fd.write('authod:' + str(item[1]) + '\n')
fd.write('star:' + str(item[2]) + '\n')
fd.write('content:' + str(item[3]) + '\n')
if fm == 'json':
temp = ('agree','authod','star','content')
for item in data:
json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
if fm == 'csv':
writer = csv.writer(fd)
for item in data:
writer.writerow(item)
2、代碼實現
注意,本程序需要用戶輸入電影 ID,用於構造初始 URL ,例如:
如果電影的鏈接地址為:https://movie.douban.com/subject/26985127/comments?status=P
那么電影 ID 為:26985127
【PS:雖然這種做法對用戶不太友好,但是由於個人水平以及時間問題,目前也還沒想到比較好的解決方法,
最初的想法是讓用戶輸入電影名稱,然后由程序自動將電影名稱映射為電影 ID,從而構造出初始 URL】
import requests
from lxml import etree
import re
import json
import csv
import time
import random
# 獲取網頁源代碼
def get_page(url):
headers = {
'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
response = requests.get(url=url,headers=headers)
html = response.text
return html
# 解析網頁源代碼,獲取下一頁鏈接
def parse4link(html,base_url):
link = None
html_elem = etree.HTML(html)
url = html_elem.xpath('//div[@id="paginator"]/a[@class="next"]/@href')
if url:
link = base_url + url[0]
return link
# 解析網頁源代碼,獲取數據
def parse4data(html):
html = etree.HTML(html)
agrees = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[1]/span/text()')
authods = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[2]/a/text()')
stars = html.xpath('//div[@class="comment-item"]/div[2]/h3/span[2]/span[2]/@title')
contents = html.xpath('//div[@class="comment-item"]/div[2]/p/span/text()')
data = zip(agrees,authods,stars,contents)
return data
# 打開文件
def openfile(fm):
fd = None
if fm == 'txt':
fd = open('douban_comment.txt','w',encoding='utf-8')
elif fm == 'json':
fd = open('douban_comment.json','w',encoding='utf-8')
elif fm == 'csv':
fd = open('douban_comment.csv','w',encoding='utf-8',newline='')
return fd
# 將數據保存到文件
def save2file(fm,fd,data):
if fm == 'txt':
for item in data:
fd.write('----------------------------------------\n')
fd.write('agree:' + str(item[0]) + '\n')
fd.write('authod:' + str(item[1]) + '\n')
fd.write('star:' + str(item[2]) + '\n')
fd.write('content:' + str(item[3]) + '\n')
if fm == 'json':
temp = ('agree','authod','star','content')
for item in data:
json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
if fm == 'csv':
writer = csv.writer(fd)
for item in data:
writer.writerow(item)
# 開始爬取網頁
def crawl():
moveID = input('請輸入電影ID:')
while not re.match(r'\d{8}',moveID):
moveID = input('輸入錯誤,請重新輸入電影ID:')
base_url = 'https://movie.douban.com/subject/' + moveID + '/comments'
fm = input('請輸入文件保存格式(txt、json、csv):')
while fm!='txt' and fm!='json' and fm!='csv':
fm = input('輸入錯誤,請重新輸入文件保存格式(txt、json、csv):')
fd = openfile(fm)
print('開始爬取')
link = base_url
while link:
print('正在爬取 ' + str(link) + ' ......')
html = get_page(link)
link = parse4link(html,base_url)
data = parse4data(html)
save2file(fm,fd,data)
time.sleep(random.random())
fd.close()
print('結束爬取')
if __name__ == '__main__':
crawl()
寫完之后,我們運行代碼試一下效果:
咦?好像有點怪怪的,怎么只有 11 頁評論?不科學呀,《一出好戲》這部電影明明有十多萬條評論的呀
我們直接用瀏覽器打開最后一個鏈接看一下:
原來,11 頁之后的評論是需要登陸之后才有權限訪問的,沒辦法,那就只好再寫一個模擬登陸唄
我們這里使用最最簡單的方法進行模擬登陸,那就是使用 Cookie,並且是手動獲取 Cookie (懶)
簡單來說,Cookie 是為了記錄用戶信息而儲存在用戶本地終端上的數據
當我們在瀏覽器上登陸后,我們登陸的信息會被記錄在 Cookie 中
之后的操作,瀏覽器會自動在請求頭中加上 Cookie,說明這是一個特定用戶發送的請求
那么怎樣獲取 Cookie 呢?也很簡單,用瀏覽器打開 豆瓣電影首頁 進行登陸,然后進行抓包就可以
最后,我們只需要把 Cookie 信息復制下來,放到請求頭中一起發送,這樣就可以繼續愉快的爬取評論啦
【PS:注意 Cookie 的有效期,獲取 Cookie 后應該盡快使用】
【爬蟲系列相關文章】