我們在爬取數(shù)據(jù)時,往往是連續(xù)爬取上百個頁面,本篇以爬取趕集網為例,爬取大規(guī)模的數(shù)據(jù)。步驟如下:
爬取1級商品鏈接
爬取2級詳情信息
爬取商品詳情頁
多進程爬取數(shù)據(jù)
一、爬取1級商品鏈接
新建一個Python文件,名字命名為my_channel_extracing,用于抓取大類商品鏈接。以抓取趕集網http://bj.ganji.com/wu/上二手商品為例,右側的各類商品便是我們需要抓取的大類商品。

①request頁面
引入requests對http://bj.ganji.com/wu/進行訪問,并輸出打印結果,查詢是否訪問成功。
代碼如下:
import requests
url='http://bj.ganji.com/wu/'
wb_data = requests.get(url)
print(wb_data.text)

②解析頁面
引入BeautifulSoup對網頁進行解析,打印輸出結果,查看網頁解析是否成功。
代碼如下:
import requests
from bs4 import BeautifulSoup
url='http://bj.ganji.com/wu/'
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text.'lxml')
print(soup)

③爬取大類鏈接
檢查需要爬取的網頁,定位鏈接位置,利用for循環(huán)輸出所爬取的鏈接。通過觀察網頁,能發(fā)現(xiàn)網頁的絕對路勁為http://bj.ganji.com,定義網頁絕對路勁,最終輸出完整的爬取頁面。
import requests
from bs4 import BeautifulSoup
host_url = 'http://bj.ganji.com'#定義網頁絕對路徑
url = 'http://bj.ganji.com/wu/'
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('#wrapper > div.content > div > div > dl > dt > a')
for link in links:
print(host_url+i.get('href'))

二、爬取2級商品鏈接
①爬取2級商品鏈接
將爬取的1級商品鏈接放入page_link,通過解析page_link,并再次利用for函數(shù),輸出2級商品鏈接。
import requests
from bs4 import BeautifulSoup
host_url = 'http://bj.ganji.com'#定義網頁絕對路徑
url = 'http://bj.ganji.com/wu/'
wb_data = requests.get(url)
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('#wrapper > div.content > div > div > dl > dt > a')
for link in links:
page_link = host_url + link.get('href')#1級網頁
wb_data = requests.get(page_link)
soup = BeautifulSoup(wb_data.text,'lxml')
type_links = soup.select('#seltion > div > dl > dd > a')
for type_link in type_links:
print(host_url + type_link.get('href'))

②整理代碼
定義一個函數(shù),將之前寫的代碼進行封裝,并在requests訪問網頁的時候,設置一個timeout。告訴 requests 在經過以 timeout 參數(shù)設定的秒數(shù)時間之后停止等待響應。例如設置timeout=6,既如果訪問網頁不成功,6秒鐘之后訪問下一個網頁。
代碼如下:
import requests
from bs4 import BeautifulSoup
def get_index_url():
urls=[]
host_url = 'http://bj.ganji.com'#定義網頁絕對路徑
url = 'http://bj.ganji.com/wu/'
wb_data = requests.get(url,timeout=6)#如果程序響應時間超過6秒,則進行下一步
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('#wrapper > div.content > div > div > dl > dt > a')
for link in links:
page_link = host_url + link.get('href')#1級網頁
wb_data = requests.get(page_link,timeout=6)
soup = BeautifulSoup(wb_data.text,'lxml')
type_links = soup.select('#seltion > div > dl > dd > a')
for type_link in type_links:
print(host_url + type_link.get('href'))#2級網頁
urls.append(host_url + type_link.get('href')) #將獲取的2級網頁存入之前設置的urls列表中
return urls
get_index_url()

三、爬取詳情頁鏈接
隨意點擊進入一個2級網頁鏈接,以http://bj.ganji.com/shouji/為例。
①新建Python文件
新建一個Python文件用于抓取詳情頁鏈接和詳情頁中需要爬取的內容。新建文件名取名為my_page_parsing。
②爬取2級頁面序列鏈接
通過觀察網頁,很容易發(fā)現(xiàn)2級網頁鏈接的序列變化是由需要爬取的url+o+數(shù)字構成的,如爬取的手機2級網頁鏈接,第二頁的url鏈接為http://bj.ganji.com/shouji/o2/,第三頁的url鏈接為http://bj.ganji.com/shouji/o3/,以此類推。因此,可以利用range和format對爬取2級網頁鏈接進行序列構造。
代碼如下:
import requests
url = 'http://bj.ganji.com/shouji/'
for page in range(30):
list_view = '{}o{}'.format(url,page)
print(list_view)

③爬取詳情頁鏈接
解析上一步中爬取到的2級網頁序列鏈接,通過瀏覽器檢查定位,獲取詳情頁的鏈接,并篩選出【轉轉】鏈接。
import requests
from bs4 import BeautifulSoup
url = 'http://bj.ganji.com/shouji/'
for page in range(30):
list_view = '{}o{}'.format(url,page)#獲取詳情頁
print(list_view)
wb_data = requests.get(list_view)
soup = BeautifulSoup(wb_data.text,'lxml')
links = soup.select('td.t > a')
for link in links:
item_link = link.get('href')
if 'zhuanzhuan' in item_link: # 過濾掉推選鏈接,僅保留轉轉鏈接
print(item_link)

④將詳情頁鏈接存入MongDB數(shù)據(jù)庫
引入MongDB,進一步篩選掉無法訪問的頁面,將爬取的詳情頁鏈接存入數(shù)據(jù)庫,并定義一個函數(shù),將代碼進行封裝。
import requests
from bs4 import BeautifulSoup
import pymongo
import time
client = pymongo.MongoClient('localhost',27017)
ganji = client['ganji']
sheet1_url_list = ganji['sheet1_url_list']
def get_link_from(url):
for page in range(30):
list_view = '{}o{}'.format(url,page)#獲取詳情頁
wb_data = requests.get(list_view)
soup = BeautifulSoup(wb_data.text,'lxml')
time.sleep(3)
links = soup.select('td.t > a')
if soup.select('div.noinfotishi'):#篩選無法訪問的頁面
return
else:
for link in links:
item_link = link.get('href')
if 'zhuanzhuan' in item_link:#過濾掉推選頁面,僅保留轉轉頁面
print(item_link)
sheet1_url_list.insert_one({'url':item_link})
get_link_from('http://bj.ganji.com/shouji/')

⑤整理完善代碼
利用try-except語句、引入time第三方庫,完善當前的代碼。通過在get_link_from定義的函數(shù)處新增times變量,利用if、try-excepy實現(xiàn)如下功能:當訪問頁面超出設定次數(shù)時,便直接返回。
try-except語句的用法是:
try:
<語句> #運行代碼
except:
<異常處理的語句> #如果在try部份引發(fā)了異常,運行異常處理語句
修改之后的代碼如下:
import requests
from bs4 import BeautifulSoup
import pymongo
import time
client = pymongo.MongoClient('localhost',27017)
ganji = client['ganji']
sheet1_url_list = ganji['sheet1_url_list']
def get_link_from(url,times=0):
if times > 10:#當訪問次數(shù)超過10次仍然無法讀取網頁時,則直接返回
return
for page in range(3):
list_view = '{}o{}'.format(url,page)#獲取詳情頁
try:
wb_data = requests.get(list_view,timeout=6)
except:
return get_link_from(url,times+1)#每次訪問不成功,times便增加一次
soup = BeautifulSoup(wb_data.text,'lxml')
time.sleep(3)
links = soup.select('td.t > a')
if soup.select('div.noinfotishi'):#篩選無法訪問的頁面
return
else:
for link in links:
item_link = link.get('href')
if 'zhuanzhuan' in item_link:#過濾掉推選頁面,僅保留轉轉頁面
print(item_link)
sheet1_url_list.insert_one({'url':item_link})
get_link_from('http://bj.ganji.com/shouji/')

⑥爬取詳情頁信息并存儲
爬取詳情頁中標題、價格、地區(qū)和瀏覽量的相關信息,并存入MongDB數(shù)據(jù)庫中。具體的方法和爬取鏈接相差不大,直接貼代碼出來。
import requests
from bs4 import BeautifulSoup
import pymongo
import time
client = pymongo.MongoClient('localhost',27017)
ganji = client['ganji']
sheet1_url_list = ganji['sheet1_url_list']
sheet2_item_info = ganji['sheet2_item_info']
def get_link_from(url,times=0):
if times > 10:#當訪問次數(shù)超過10次仍然無法讀取網頁時,則直接返回
return
for page in range(3):
list_view = '{}o{}'.format(url,page)#獲取詳情頁
#print(list_view)
try:
wb_data = requests.get(list_view,timeout=6)
except:
return get_link_from(url,times+1)#每次訪問不成功,times便增加一次
soup = BeautifulSoup(wb_data.text,'lxml')
time.sleep(3)
links = soup.select('td.t > a')
if soup.select('div.noinfotishi'):#篩選無法訪問的頁面
return
else:
for link in links:
item_link = link.get('href')
if 'zhuanzhuan' in item_link:#過濾掉推選頁面,僅保留轉轉頁面
print(item_link)
sheet1_url_list.insert_one({'url':item_link})
def get_item_info_from(url,times=0):
if times>10:
return
try:
wb_data = requests.get(url,timeout=6)
except:
return get_item_info_from(url,times+1)
soup = BeautifulSoup(wb_data.text, 'lxml')
title = soup.select('div.info_lubotu.clearfix > div.box_left_top > h1')
price = soup.select('div.info_lubotu.clearfix > div.info_massege.left > div.price_li > span > i')
area = soup.select('div.info_lubotu.clearfix > div.info_massege.left > div.palce_li > span > i')
view = soup.select('body > div.content > div > div.box_left > div.info_lubotu.clearfix > div.box_left_top > p > span.look_time')
data = {
'title': title[0].text if title else None,
'price': price[0].text if price else 0,
'area': area[0].text if area else None,
'view': view[0].text if view else None,
'url':url
}
sheet2_item_info.insert_one(data)
print(data)

需要注意的是,在構建data字典填入數(shù)據(jù)時,用到了if-else語句,這個一定要加上,否則在運行時,如果沒有找到相關信息,是會報錯的。這個語句的意思是,如果能找到爬取信息的第一個元素,則填入元素,若爬取的網頁沒有我們需要的信息,則填為空。
4、多進程爬取數(shù)據(jù)
完成上面的工作,我們的主要任務就已經完成了。接下來是利用多進程,爬取我們需要的信息。
什么是多進程多線程呢?多進程和多線程是一種并發(fā)技術,就是可以讓你在同一時間同時執(zhí)行多條任務的技術。進程是程序在計算機上的一次執(zhí)行活動,線程就是把一個進程分為很多片,每一片都可以是一個獨立的流程。用侯爵老師課程中的類比來看,可以將電腦比喻為一家餐廳,餐廳內的餐桌數(shù)就是電腦CPU的內核數(shù),當CPU的一個內核運行一個程序時,就是單進程單線程。當CPU的一個內核運行多個程序時,就是單進程多線程。當CPU調用多個內核運行一個程序時,就是多進程單線程。當CPU調用多個內核運行多個程序時,就是多進程多線程。
單進程單線程:指一張桌子上一個人吃飯
單進程多線程:指一張桌子多個人吃飯
多進程單線程:指多張桌子,每張桌子上只有一個人吃飯
多進程多線程:指多張桌子,每張桌子上多個人吃飯
本次采用的方法是多進程單線程爬取數(shù)據(jù),新建一個Python文件命名為my_main。
代碼如下:
from multiprocessing import Pool #引入多進程庫,幫助電腦調用CPU的多個內核
from my_page_parsing import get_link_from,get_item_info_from,sheet1_url_list#引入之前定義的函數(shù)
if __name__ =="__main__":#避免產生名稱混亂。作用為:如果模塊是被直接運行的,則代碼被運行,如果模塊是被導入的,則代碼不被運行
pool = Pool() #創(chuàng)建進程池
pool.map(get_item_info_from,[i['url'] for i in sheet1_url_list.find()])#將shee1_url_list中的鏈接依次放入get_item_info_from函數(shù)中
pool.close()#關閉pool,使其不再接受新的任務
pool.join()#主進程阻塞,等待子進程的退出

大功告成,之后就是漫長的爬取過程了。還需要提醒的是,在運行my_main時,記得將my_page_parsing中,運行函數(shù)時使用的特定url取掉,否則就只是爬取指定的url了。
總結:
1.在爬取數(shù)據(jù)量巨大的網站時,可以將爬取過程分步驟逐個擊破,① 爬取1級商品鏈接,②爬取2級詳情信息,③爬取商品詳情頁,④構造多進程代碼爬取數(shù)據(jù)。
2.為了防止爬取出錯,可以利用times、timeout、try-except等方法完善代碼,這些細微的調整是需要時間積累的,通過不斷地去實踐,不斷總結其中規(guī)律。
3.需要注意的坑:①if __name__ =="__main__":是一個固定的寫法,并不會因為Python文件的命名而改變;②爬取詳情頁中標題、價格、地區(qū)和瀏覽量的相關信息,構建data字典填入數(shù)據(jù)時,用到了if-else語句,這是為了防止出現(xiàn)空值而報錯,一定要加上。
PS:以上的學習筆記來源于侯爵老師的《0基礎Python爬蟲:四周實現(xiàn)爬蟲實戰(zhàn)》課程,歡迎感興趣的朋友購買學習。
