說明:我在寫這篇博客時有點着急,前半部分的代碼都沒有行號,后半部分的代碼有行號,不要錯把行號看成是代碼(應該沒有人會犯這種錯誤)。后面大半部分都是數據的截圖,可以直接忽略。
把總結寫在前面:不得不說,爬蟲真的是一個抓取網頁信息的好手段,但是它的局限性很大,Web 信息的巨大容量使得爬蟲在給定時間內只能下載少量網頁,即使能夠提取全部頁面,也沒有足夠的空間來存儲。爬行效率低,無法在單位時間內盡可能多的獲取高質量頁面。
1.首先確定爬取數據的網址,這里我是在谷歌中打開當當網“圖書暢銷榜”,網址:http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-1
此時顯示的是第一頁,第二頁網址的最后一個數字為2,以此類推。所以,爬取所有頁碼只需使用for語句構造循環,按頁碼依次進行網址的拼接並發起請求即可
演示代碼:
import requests import pandas as pd from bs4 import BeautifulSoup headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'} #模擬瀏覽器的身份驗證信息 for i in range(1,26): url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-{i}'#循環生成的每一頁網址
2.經確認,數據是動態加載的,可以用響應對象的text屬性獲取網頁源代碼,再從源代碼中提取數據。在上述代碼的for語句構造的循環內部繼續添加如下代碼:
response=requests.get(url=url,headers=headers,timeout=10)#對25頁的不同網址循環發起請求,設置超時等待為10秒 html_content=response.text#獲取網頁源代碼 soup=BeautifulSoup(html_content,'lxml')#將網頁源代碼實比例轉化為BeautifulSoup對象 parse_html(soup)#調用自定義函數提取BeautifulSoup對象中的數據,具體代碼見后 print(f'第{i}頁爬取完畢')
3.分析要提取數據的標簽。要提取的排行榜數據位於class屬性值為bang_list的<ul>標簽下的多個<li>標簽中。
4.編寫提取數據的代碼。
創建字典data_info,編寫自定義函數parse_html(),從BeautifulSoup對象中提取數據並添加到字典中。
演示代碼:
data_info = {'圖書排名': [], '圖書名稱': [], '圖書作者': [], '圖書出版時間': [], '圖書出版社': [], '圖書價格': []}#新建一個空字典
def parse_html(soup):#解析每一個BeautifulSoup對象 li_list = soup.select('.bang_list li') # 通過層級選擇器定位class屬性值為bang_list的標簽下的所有<li>標簽 for li in li_list:#將從每一個<li>標簽中解析到的數據添加到字典data_info相應鍵的對應列表中 data_info['圖書排名'].append(li.select('.list_num ')[0].text.replace('.', '')) data_info['圖書名稱'].append(li.select('.name a')[0].text) data_info['圖書作者'].append(li.select('.publisher_info ')[0].select('a')[0].text) data_info['圖書出版時間'].append(li.select('.publisher_info span')[0].text) data_info['圖書出版社'].append(li.select('.publisher_info ')[1].select('a')[0].text) data_info['圖書價格'].append(float(li.select('.price .price_n')[0].text.replace('¥', '')))
注意:在最終的代碼文件中,定義parse_html()函數的代碼需位於調用該函數的代碼之前。
5.缺失值和重復值的處理。
先用pandas模塊將字典轉換為DataFrame對象格式,再判斷缺失值和重復值。
演示代碼:
1 book_info=pd.DataFrame(data_info) 2 print(book_info.isnull())#缺失值判斷 3 print(book_info.duplicated())#重復值判斷
6.一異常值的處理。本文只研究價格在100元以下的圖書,所以需要將價格高於100元的圖書刪除。
演示代碼:
1 book_info['圖書價格'][book_info['圖書價格']>100]=None#將大於100的圖書價格替換為空值 2 book_info=book_info.dropna()#刪除有空值的一行數據
7.保存爬取的數據,儲存為csv文件。
演示代碼:
1 book_info.to_csv('當當網圖書銷售排行.csv',enco ding='utf-8',index=False)
完整代碼在這里:
1 import requests 2 import pandas as pd 3 from bs4 import BeautifulSoup 4 headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36'} 5 6 data_info = {'圖書排名': [], '圖書名稱': [], '圖書作者': [], '圖書出版時間': [], '圖書出版社': [], '圖書價格': []} 7 def parse_html(soup): 8 li_list = soup.select('.bang_list li') 9 for li in li_list: 10 data_info['圖書排名'].append(li.select('.list_num ')[0].text.replace('.', '')) 11 data_info['圖書名稱'].append(li.select('.name a')[0].text) 12 data_info['圖書作者'].append(li.select('.publisher_info ')[0].select('a')[0].text) 13 data_info['圖書出版時間'].append(li.select('.publisher_info span')[0].text) 14 data_info['圖書出版社'].append(li.select('.publisher_info ')[1].select('a')[0].text) 15 data_info['圖書價格'].append(float(li.select('.price .price_n')[0].text.replace('¥', ''))) 16 17 for i in range(1, 26): 18 url = f'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-{i}' 19 response = requests.get(url = url, headers = headers, timeout = 10) 20 html_content = response.text 21 soup = BeautifulSoup(html_content, 'lxml') 22 parse_html(soup) 23 print(f'第{i}頁爬取完畢') 24 25 book_info = pd.DataFrame(data_info) 26 print(book_info.isnull()) 27 print(book_info.duplicated()) 28 29 book_info['圖書價格'][book_info['圖書價格'] > 100] = None 30 book_info = book_info.dropna() 31 32 book_info.to_csv('當當網圖書銷售排行.csv', encoding = 'utf-8', index = False)
結果如下: