目錄
1. 一次性爬取多頁豆瓣繪本數據
Python手記-7中實現了單頁爬取數據,本節來試試多頁數據爬取,案例背景爲豆瓣圖書網頁實現繪本的多頁數據爬取,先看看網址信息:
複製出來:
第一頁:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=0&type=T
第二頁:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=20&type=T
第三頁:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=40&type=T
……
在原網址裏的中文字符“繪本”複製出來變成了“%E7%BB%98%E6%9C%AC”,這個是URL編碼問題,用哪個都不影響爬取;
觀察推斷url變化規律,變化的只是start參數值,且start值是首項爲0,公差爲20的等差遞增數列:
第i頁:https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=(i-1)* 20&type=T
下面就粗略的試試看:
# -*- coding: utf-8 -*-
# @Time : 2020/4/27 14:14
# @Author : ChengYu
# @File : requests_getbooks.py
import requests
import re
# 加上headers用來告訴網站這是通過一個瀏覽器進行的訪問
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/81.0.4044.122 Safari/537.36'}
# page_index起始值爲0,爬取20頁
for page_index in range(20):
url = 'https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=' + str(page_index * 20) + '&type=T'
# url = 'https://book.douban.com/tag/繪本?start=' + str(page_index * 20) + '&type=T'
# 獲取網頁源碼
res = requests.get(url, headers=headers).text
# 獲取繪本鏈接、作者信息、書名、評價數、簡介
p_href = '<h2 class="">.*?<a href="(.*?)"'
href = re.findall(p_href, res, re.S)
p_info = '<div class="pub">(.*?)</div>'
info = re.findall(p_info, res, re.S)
# p_title = '<h2 class="">.*?>(.*?)</a>'
p_title = '<h2 class="">.*?title="(.*?)"'
title = re.findall(p_title, res, re.S)
p_appraise = '<span class="pl">(.*?)</span>'
appraise = re.findall(p_appraise, res, re.S)
# p_detail = '<div class="info">.*?<p>(.*?)</p>'
# detail = re.findall(p_detail, res, re.S)
for i in range(len(title)):
title[i] = title[i].strip()
info[i] = info[i].strip()
appraise[i] = appraise[i].strip()
href[i] = href[i].strip()
# detail[i] = detail[i].strip()
# 出去換行符
# detail[i] = re.sub('\n', '', detail[i])
print(str(i + 1) + '.' + title[i] + ' ' + href[i] + '\n' + info[i] + '\n' + appraise[i])
print('第' + str(page_index + 1) + '頁爬取成功')
“run”一下看看結果,書名、鏈接、作者/出版等信息、評論數了然在列:
2. 爬取多頁豆瓣繪本數據存入CSV文件
CSV模塊是實現以CSV格式讀取和寫入表格數據的類,CSV模塊主要功能(官文:https://docs.python.org/3.8/library/csv.html):
(1)csv.reader(csvfile, dialect='excel', **fmtparams)/csv.writer(csvfile, dialect='excel', **fmtparams):csvfile可以是支持迭代器協議並在每次__next__()
調用其方法時都返回字符串的任何對象- 文件對象和列表對象均適用,簡言之Excel或者CSV文件都可以;其中的Dialects and Formatting參數:
- Dialect.delimiter:分隔字段的字符,默認爲',';
- Dialect.doublequote:雙引號,當單引號已經被定義,並且quoting參數不是QUOTE_NONE的時候,使用雙引號表示引號內的元素作爲一個元素使用;
- Dialect.escapechar:當quoting 爲QUOTE_NONE時,指定一個字符使的不受分隔符限值,默認爲None,將禁用轉義;
- Dialect.lineterminator:行分隔符,默認爲'\r\n';
- Dialect.quotechar:引用符,用於引用包含特殊字符(例如定界符或quotechar)或包含換行符的字段,默認爲'"'。
- Dialect.quoting:控制csv中的引號常量,可選QUOTE_*常量,默認爲QUOTE_MINIMAL;
- Dialect.skipinitialspace:如果爲True,則分隔符後的空白將被忽略,默認值爲False;
- Dialect.strict:如果爲True,則Error在CSV輸入錯誤時引發異常,默認值爲False。
(2)csv.DictReader(f,fieldnames=None, restkey=None, restval=None, dialect='excel', *args, **kwds)/csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds):
- fieldnames:字段名列表,默認是第一行的數據;
- restkey:當實際的字段數大於字段名數量時,多出來的字段名稱就是這個restkey指定(默認爲
None
); - restval:如果非空白行的字段少於字段名,則缺少的值將填充爲restval的值(默認爲
None
); - extrasaction:當數據中有額外的字段時所採取的操作,默認爲’raise’,即拋異常,有時候這種情況比較煩,所以還可以設置成’ignore’直接忽略。
(3)csvreader.fieldnames:字段名稱,默認是第一行的數據。
(4)csvwriter.writerow(row)/csvwriter.writerows(rows):單行寫入/多行寫入。
(5)DictWriter.writeheader():寫入字段名稱,也就是csv頭部信息。
寫入用法示例(偷懶了,來源官文):
import csv
with open('names.csv', 'w', newline='') as csvfile:
fieldnames = ['first_name', 'last_name']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})
讀取用法示例(偷懶了,來源官文):
import csv
with open('some.csv', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
學以致用,接下來在前面的案例基礎上改動一下,繼續爬取豆瓣繪本前10頁的書本數據,並稍微美化一下輸出,以CSV文件格式保存數據。
# -*- coding: utf-8 -*-
# @Time : 2020/4/27 14:14
# @Author : ChengYu
# @File : requests_getbooks.py
import requests
import re
import csv
# 加上headers用來告訴網站這是通過一個瀏覽器進行的訪問
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/81.0.4044.122 Safari/537.36'}
# 初始化csv文件
with open('books.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['序號', '書名', '評分', '評論人數', '作者/出版社/出版日期/價格', '鏈接'])
# bookinfo = []
count = 1
# page_index起始值爲0,爬取10頁
for page_index in range(10):
url = 'https://book.douban.com/tag/%E7%BB%98%E6%9C%AC?start=' + str(page_index * 20) + '&type=T'
# url = 'https://book.douban.com/tag/繪本?start=' + str(page_index * 20) + '&type=T'
# 獲取網頁源碼
res = requests.get(url, headers=headers).text
# 獲取繪本鏈接、作者信息、書名、評價數、簡介
p_href = '<h2 class="">.*?<a href="(.*?)"'
href = re.findall(p_href, res, re.S)
p_info = '<div class="pub">(.*?)</div>'
info = re.findall(p_info, res, re.S)
# p_title = '<h2 class="">.*?>(.*?)</a>'
p_title = '<h2 class="">.*?title="(.*?)"'
title = re.findall(p_title, res, re.S)
p_grade = '<span class="rating_nums">(.*?)</span>'
grade = re.findall(p_grade, res, re.S)
p_appraise = '<span class="pl">(.*?)</span>'
appraise = re.findall(p_appraise, res, re.S)
# p_detail = '<div class="info">.*?<p>(.*?)</p>'
# detail = re.findall(p_detail, res, re.S)
for j in range(len(title)):
title[j] = title[j].strip()
info[j] = info[j].strip()
appraise[j] = appraise[j].strip().replace('(', '').replace('人評價)', '')
grade[j] = grade[j].strip()
href[j] = href[j].strip()
writer.writerow([count, title[j], grade[j], appraise[j], info[j], href[j]])
# print(title[i], info[i], href[i], appraise[i])
# bookinfo.append([title[i], info[i], href[i], appraise[i]])
# print('第' + str(i + 1) + '頁爬取成功')
count += 1
# 注意csvfile.close()縮進問題,這應該是與for同級別,否則可能造成:ValueError: I/O operation on closed file.
csvfile.close()
“run”之後會在當前project目錄生成books.csv,內容顯示如下,序號不再是1-20的循環,而是從1一直遞增到爬取結束,評論人數從(558486人評價)變爲558486,增加評分列爬取,當然也可以增加其他的爬取內容,無外乎就是正則和re庫方法的使用。
叮!淺嘗輒止,這個主題暫時下課!