Python學習筆記7——爬取大規(guī)模數(shù)據(jù)

我們在爬取數(shù)據(jù)時,往往是連續(xù)爬取上百個頁面,本篇以爬取趕集網為例,爬取大規(guī)模的數(shù)據(jù)。步驟如下:

  1. 爬取1級商品鏈接

  2. 爬取2級詳情信息

  3. 爬取商品詳情頁

  4. 多進程爬取數(shù)據(jù)

一、爬取1級商品鏈接

新建一個Python文件,名字命名為my_channel_extracing,用于抓取大類商品鏈接。以抓取趕集網http://bj.ganji.com/wu/上二手商品為例,右側的各類商品便是我們需要抓取的大類商品。

大類鏈接.jpg

①request頁面
引入requests對http://bj.ganji.com/wu/進行訪問,并輸出打印結果,查詢是否訪問成功。
代碼如下:

import requests

url='http://bj.ganji.com/wu/'
wb_data = requests.get(url)
print(wb_data.text)
requests頁面

②解析頁面
引入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'))
爬取2級鏈接

②整理代碼
定義一個函數(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級網頁序列鏈接.jpg

③爬取詳情頁鏈接
解析上一步中爬取到的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)
爬取詳情頁鏈接.jpg

④將詳情頁鏈接存入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/')
將詳情頁鏈接存入數(shù)據(jù)庫

⑤整理完善代碼

利用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)
商品信息存入數(shù)據(jù)庫

需要注意的是,在構建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()#主進程阻塞,等待子進程的退出
多進程爬取數(shù)據(jù)

大功告成,之后就是漫長的爬取過程了。還需要提醒的是,在運行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)》課程,歡迎感興趣的朋友購買學習。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容