第二周/第二周實戰(zhàn)作業(yè): 爬取10萬商品數(shù)據(jù)

1. 引言

Paste_Image.png
標題 說明
網(wǎng)址 http://sh.ganji.com/wu/
要求1 點進來, 拉到頁面底部
要求2 需要爬取趕集網(wǎng)-上海-二手市場的所有類目的商品信息
要求3 點進來的列表頁, 抓取個人類目下的全部帖子
Paste_Image.png

Paste_Image.png

2. 分析

  • 分類查找:

檢查元素, 輸入.fenlei > dt > a可以等到全部分類鏈接

Paste_Image.png

頁數(shù)隨最后一個數(shù)字變化

多了a2字符, 頁數(shù)隨最后一個數(shù)字變化

  • 列表頁中鏈接查找:

.ft-tit找到全部鏈接, 接著過慮掉包含click關鍵字的推廣商品和zhuanzhuan商品

Paste_Image.png

  • 列表尾部:

只有5個條目:


Paste_Image.png
  • 已賣完商品返回的頁面狀態(tài)碼為404
  • 詳情信息抓取采用斷點續(xù)傳和多進程

3. 實現(xiàn)部分

3.1 基礎模塊
# vim spider_ganji.py  // 基礎模塊
#!/usr/bin/env python3                                                                                                       
# -*- coding: utf-8 -*-                                                                                                      
                                                                                                                             
__author__ = 'jhw'                                                                                                           
                                                                                                                             
                                                                                                                             
from bs4 import BeautifulSoup                                                                                                
from pymongo import MongoClient                                                                                              
import requests                                                                                                              
                                                                                                                             
                                                                                                                             
headers = {                                                                                                                  
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36'
    'Accept-Encoding': 'gzip, deflate',                                                                                      
    'Accept-Language': 'zh-CN,zh',                                                                                           
    'Connection': 'keep-alive',                                                                                              
}                                                                                                                            
                                                                                                                             
# 自定義要抓取區(qū)域, 拼音簡寫                                                                                                 
locale = 'sh'                                                                                                                
                                                                                                                             
client = MongoClient('10.66.17.17', 27017)                                                                                   
database = client['ganji']                                                                                                   
# 區(qū)域分表存入mongodb                                                                                                        
url_info = database['{}_ershou_url'.format(locale)]                                                                          
# 已抓取的url存入此表                                                                                                        
url_info_exists = database['{}_ershou_url_exists'.format(locale)]                                                            
# 商品信息存儲表                                                                                                             
item_info = database['{}_ershou_item'.format(locale)]                                                                        
# 將要抓取的全部url放入此列表中                                                                                              
url_info_list = [item['url'] for item in url_info.find()]                                                                    
# 抓取完畢的url放入此列表中                                                                                                  
url_info_exists_list = [item['url'] for item in url_info_exists.find()]                                                      
                                                                                                                             
                                                                                                                             
# 定義獲取主分類鏈接的函數(shù)                                                                                                   
def get_url_index(locale):                                                                                                   
                                                                                                                             
    # 鏈接根據(jù)上面給定的區(qū)域決定                                                                                             
    url = 'http://{}.ganji.com/wu/'.format(locale)                                                                           
    data = requests.get(url, headers=headers)                                                                                
    soup = BeautifulSoup(data.text, 'lxml')                                                                                  
    links = soup.select('.fenlei > dt > a')                                                                                  
                                                                                                                             
    # 定義存儲分類鏈接的列表                                                                                                 
    L = []                                                                                                                   
    for link in links:                                                                                                       
        # 組合一條完整的分類鏈接                                                                                             
        link_pre = url.split('wu/')[0]+'{}/'.format(link.get('href').strip('/'))                                             
        # 將分類鏈接存入分類列表                                                                                             
        L.append(link_pre)                                                                                                   
                                                                                                                             
    # 返回分類鏈接列表, 用作多進程                                                                                           
    return L

                                                                                                                             
# 定義循環(huán)獲取全部url的商品信息函數(shù), 主分類鏈接傳入                                                                          
def get_url_from_list(url, who_sells=''):                                                                                    
                                                                                                                             
    # 主分類鏈接循環(huán)加入頁數(shù)                                                                                                 
    for page in range(1, 200):                                                                                               
        # 一條完整的帶有頁數(shù)的商品鏈接                                                                                       
        url_full = url + '{}o{}'.format(who_sells, page)                                                                     
        # 執(zhí)行獲取商品信息函數(shù), 同時返回執(zhí)行狀態(tài)碼                                                                           
        code = get_url_info(url_full, page)                                                                                  
                                                                                                                             
        # 返回的條目少于10則判斷到了頁面尾部, 退出循環(huán)                                                                       
        if code < 10:                                                                                                        
            print('$'*20, '%s End of the pages!!!' % url_full, '$'*20, '\n\n')                                               
            break                                                                                                            
                                                                                                                             
        print('\n')                                                                                                          
                                                                                                                             
                                                                                                                             
# 定義獲取各分類頁中商品鏈接的函數(shù)                                                                                           
def get_url_info(url, page):                                                                                                 
                                                                                                                             
    # 截取出鏈接屬于哪個分類                                                                                                 
    cate = url.split('/')[-2]                                                                                                
    data = requests.get(url, headers=headers)                                                                                
                                                                                                                             
    # 講求失敗則退出                                                                                                         
    if data.status_code != 200:                                                                                              
        print('%s Request Error!!!' % data.status_code)                                                                      
    else:                                                                                                                    
        soup = BeautifulSoup(data.text, 'lxml')                                                                              
        # links = soup.select('.ft-db ul li > a')                                                                            
        links = soup.select('.ft-tit')                                                                                       
        # judge = len(soup.select('dl.list-bigpic'))                                                                         
        # 獲取頁面中商品的總條目                                                                                             
        judge = len(links)                                                                                                   
                                                                                                                             
        # 如果商品總條目為5則判斷此頁面是列表頁的最后一頁, 不再抓取                                                          
        if judge == 5:                                                                                                       
            print(cate, '-', page, "Error, We can't find anything because there is nothing to be found.")                    
        else:                                                                                                                
            for i in links:                                                                                                  
                link = i.get('href')                                                                                         
                                                                                                                             
                # 推廣商品和轉轉商品過慮掉                                                                                   
                if 'click' not in link and 'zhuanzhuan' not in link:
                    # 商品鏈接存入mongodb中負責存儲商品鏈接的表中                                                         
                    url_info.insert_one({'url': link})
                    # 打印目前抓取的是哪個分類中的哪一頁                                                                       
                    print(cate, '-', page, '=>', link)                                                                       
    
    # 返回此頁商品的總條目, get_url_from_list會用到                                                                                                                         
    return judge
                                                                                                                             
                                                                                                                             
# 定義獲取商品詳情頁信息的函數(shù)                                                                                               
def get_item_from(url):                                                                                                      
                                                                                                                             
    # 如果商品之前已經(jīng)抓取過則退出                                                                                           
    if url in url_info_exists_list:                                                                                          
        print('#'*20, '"%s" has been opened before...' % url, '#'*20)                                                        
    else:                                                                                                                    
        print(url)                                                                                                           
        data = requests.get(url, headers=headers)                                                                            
        # 頁面請求錯誤則退出                                                                                                 
        if data.status_code != 200:                                                                                          
            print('E'*20, '%s request error...', 'E'*20)                                                                     
        # 頁面返回404表示商品已賣完, 退出                                                                                    
        elif data.status_code == 404:                                                                                        
            print('W'*20, 'This article has been to Mars...', 'W'*20)                                                        
        else:                                                                                                                
            soup = BeautifulSoup(data.text, 'lxml')                                                                          
            # 商品標題                                                                                                       
            titles = soup.select('.title-name')                                                                              
            # 商品發(fā)布時間                                                                                                   
            updates = soup.select('.pr-5')                                                                                   
            # views = soup.select('#pageviews')                                                                              
            # 商品類型                                                                                                       
            types = soup.select('.det-infor > li:nth-of-type(1) > span')                                                     
            # 商品價格                                                                                                       
            prices = soup.select('.f22')                                                                                     
            # 商品區(qū)域                                                                                                       
            areas = soup.select('.det-infor > li:nth-of-type(3) > a')                                                        
            # 商品成色                                                                                                       
            degrees = soup.select('.second-det-infor > li')                                                                  
                                                                                                                             
            # 有的商品沒有發(fā)布時間, 還有時請求網(wǎng)頁獲取的時間有錯, 暫時先這么判斷                                             
            if updates:                                                                                                      
                if len(updates[0].get_text()) <= 3:                                                                          
                    update = None                                                                                            
                else:                                                                                                        
                    update = updates[0].get_text().strip().split()[0]                                                        
            else:                                                                                                            
                update = None                                                                                                
                                                                                                                             
            data = {                                                                                                         
                'title': titles[0].get_text() if titles else None,                                                           
                'update': update,                                                                                            
                'type': types[0].get_text().replace('\n', '').replace(' ', '') if types else None,                           
                'price': int(prices[0].get_text()) if prices else 0,                                                         
                'area': [i.get_text().strip() for i in areas] if areas else None,                                            
                'degree': degrees[0].get_text().split('\n')[-1].replace(' ', '') if degrees else None,                       
                'cate': url.split('/')[-2],                                                                                  
            }                                                                                                                
                                                                                                                             
            print(data)
            # 商品信息存入mongodb                                                                                            
            item_info.insert_one(data)                                                                                       
            # 商品信息抓取完后, 將此商品的鏈接存入mongodb中存放已經(jīng)抓取完畢的url表中                                         
            url_info_exists.insert_one({'url': url})                                                                         
                                                                                                                             

# 獲取主分類鏈接列表                                                                                                                             
url_index = get_url_index(locale)
3.2 抓取全部商品鏈接
  # vim main.py  //程序入口
#!/usr/bin/env python3                                                                                                       
# -*- coding: utf-8 -*-                                                                                                      
                                                                                                                             
__author__ = 'jhw'                                                                                                           
                                                                                                                             
                                                                                                                             
# 從自定義模塊中導入獲取商品鏈接的函數(shù)和主分類列表                                                                           
from spider_ganji import get_url_from_list, url_index                                                                        
# 從自定義模塊中導入獲取商品詳情的函數(shù)和商品鏈接列表                                                                         
# from spider_ganji import get_item_from, url_info_list                                                                        
# 導入多進程模塊                                                                                                             
from multiprocessing import Pool                                                                                             
                                                                                                                             
                                                                                                                             
if __name__ == '__main__':                                                                                                   
    pool = Pool()                                                                                                            
    # 多進程獲取全部商品鏈接                                                                                                 
    pool.map(get_url_from_list, url_index)                                                                                 
    # 多進程獲取全部商品詳情信息                                                                                             
    # pool.map(get_item_from, url_info_list)                                                                                   
    # 調(diào)用join()之前必須先調(diào)用close(), 調(diào)用close()之后就不能繼續(xù)添加新的Process了                                            
    pool.close()                                                                                                             
    # 對Pool對象調(diào)用join()方法會等待所有子進程執(zhí)行完畢                                                                       
    pool.join()
# python3 main.py  // 開啟多進程抓取商品鏈接, 4核CPU開啟了4個進程
shouji - 2 => http://sh.ganji.com/shouji/1525463821x.htm
shouji - 2 => http://sh.ganji.com/shouji/1637265573x.htm
shouji - 2 => http://sh.ganji.com/shouji/1469334107x.htm
.
jiadian - 3 => http://sh.ganji.com/jiadian/2181455484x.htm
jiadian - 3 => http://sh.ganji.com/jiadian/2181302594x.htm
jiadian - 3 => http://sh.ganji.com/jiadian/1899011509x.htm
.
jiaju - 3 => http://sh.ganji.com/jiaju/2182971064x.htm
jiaju - 3 => http://sh.ganji.com/jiaju/2170617991x.htm
jiaju - 3 => http://sh.ganji.com/jiaju/2109235459x.htm
.
bangong - 3 => http://sh.ganji.com/bangong/2050123549x.htm
bangong - 3 => http://sh.ganji.com/bangong/2207236120x.htm
bangong - 3 => http://sh.ganji.com/bangong/1880908704x.htm
3.3 抓取全部商品詳情信息
# vim main.py    // 程序入口
# -*- coding: utf-8 -*-                                                                                                      
                                                                                                                             
__author__ = 'jhw'                                                                                                           
                                                                                                                             
                                                                                                                             
# 從自定義模塊中導入獲取商品鏈接的函數(shù)和主分類列表                                                                           
# from spider_ganji import get_url_from_list, url_index                                                                      
# 從自定義模塊中導入獲取商品詳情的函數(shù)和商品鏈接列表                                                                         
from spider_ganji import get_item_from, url_info_list                                                                        
# 導入多進程模塊                                                                                                             
from multiprocessing import Pool                                                                                             
                                                                                                                             
                                                                                                                             
if __name__ == '__main__':                                                                                                   
    pool = Pool()                                                                                                            
    # 多進程獲取全部商品鏈接                                                                                                 
    # pool.map(get_url_from_list, url_index)                                                                                 
    # 多進程獲取全部商品詳情信息                                                                                             
    pool.map(get_item_from, url_info_list)                                                                                   
    # 調(diào)用join()之前必須先調(diào)用close(), 調(diào)用close()之后就不能繼續(xù)添加新的Process了                                            
    pool.close()                                                                                                             
    # 對Pool對象調(diào)用join()方法會等待所有子進程執(zhí)行完畢                                                                       
    pool.join()
# python3 main.py  //開啟多進程抓取商品詳情信息, 4核CPU開啟了4個進程
#################### "http://sh.ganji.com/meironghuazhuang/2197468267x.htm" has been opened before... ####################
#################### "http://sh.ganji.com/meironghuazhuang/2119557406x.htm" has been opened before... ####################
#################### "http://sh.ganji.com/meironghuazhuang/2118152395x.htm" has been opened before... ####################
#################### "http://sh.ganji.com/xianzhilipin/2022660697x.htm" has been opened before... ####################
.
http://sh.ganji.com/jiadian/2196681538x.htm
{'title': '出售5.2KG海爾滾筒洗衣機 - 500元', 'type': '大家電', 'degree': None, 'price': 500, 'cate': 'jiadian', 'update': '07-06', 'area': ['上海', '長寧', '中山公園']}
http://sh.ganji.com/jiadian/2205076770x.htm
{'title': '品牌辦公家具大量低價拋售!震旦、美時、歐美、勵致、天壇等! - 88元', 'type': '其他辦公家具', 'degree': '95成新,可送貨', 'price': 88, 'cate': 'bangong', 'update': None, 'area': ['上海', '浦東', '八佰伴']}
.
http://sh.ganji.com/bangong/1660785908x.htm
{'title': '進口音箱功放機投影機空調(diào)3P紅木辦公桌書柜茶桌房子賣了搬家。 - 2500元', 'type': '書柜', 'degree': '95成新,可送貨', 'price': 2500, 'cate': 'jiadian', 'update': '07-06', 'area': ['上海', '浦東']}
http://sh.ganji.com/jiadian/2205106758x.htm
{'title': '前臺,移動柜子,辦公椅子 - 300元', 'type': '前臺桌', 'degree': '8成新,不包送貨', 'price': 300, 'cate': 'jiaju', 'update': '07-05', 'area': ['上海', '普陀', '曹楊新村']}

4. 總結

  • 多進程利用map由主函數(shù)作用于列表
  • 循環(huán)執(zhí)行的程序可由返回的狀態(tài)來決定退出與否
  • Pool的默認大小是CPU的核心數(shù),因此,最多同時執(zhí)行4個進程。這是Pool有意設計的限制,并不是操作系統(tǒng)的限制。如果改成:
    p = Pool(5)
    就可以同時跑5個進程。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容