python大數據-多線程數據抓取-58同城轉轉網的二手產品-斷點續傳

目標

多線程數據抓取-58同城轉轉網的二手產品

實作

1. 建立一個項目

新建一個項目58tongcheng1

2. 觀察頁面特征

不同頁面的不同規則問題
分頁問題

3. 設計工作流程

首先,在主列表頁爬取所有商品的URL,存儲在mongodb中,在數據庫中建立對應的URL_list (爬蟲1)
然后,詳情頁具體產品的信息,存儲在數據庫item_info中 (爬蟲2)

爬蟲2應把 爬蟲1抓取的并已存在數據庫中的URL取出來,依次讀取詳情頁,獲得所要信息,再把這些信息存儲在item_info這個表單中

4. 創建channel_extract.py獲取每個分類產品的鏈接

from bs4 import BeautifulSoup
import requests

start_url = 'http://bj.58.com/sale.shtml'
url_host = 'http://bj.58.com'

def get_channel_urls(url):
    wb_data = requests.get(start_url)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    links = soup.select('ul.ym-submnu > li > b > a')  # 尋找該標簽時比較麻煩,因為它是hover顯示
    print(links)
    for link in links:
        page_url = url_host + str(link.get('href'))
        print(page_url)

get_channel_urls(start_url)  # 通過它打印出所有的URL

把所有的URL集中起來建立一個新的長字符串

channel_list = '''
http://bj.58.com/shouji/
http://bj.58.com/tongxunyw/
http://bj.58.com/danche/
http://bj.58.com/diandongche/
http://bj.58.com/fzixingche/
http://bj.58.com/sanlunche/
http://bj.58.com/peijianzhuangbei/

5. 創建page_parsing.py獲取產品詳情

from bs4 import BeautifulSoup
import requests
import time
import pymongo

client = pymongo.MongoClient('localhost', 27017)
chengxu = client['chengxu']
url_list = chengxu['url_list3']
item_info = url_list['item_info3']

# spider 1 爬取首頁中顯示的類目中,一個類目下的所有商品的鏈接
def get_links_from(channel,pages,who_sells=0): # who_sells = 0表示個人,1表示商家
    #http://bj.58.com/shouji/1/pn2/
    list_view = '{}{}/pn{}/'.format(channel,str(who_sells),str(pages)) # 找網頁規律的時候,剛刷新和點擊后的相同頁面的網址會有變化,但頁面相同,它們是等價的,所以找頁面規律時要多點擊或刷新來找
    wb_data = requests.get(list_view)
    time.sleep(1)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    if soup.find('td','t'):  # 一個類目的頁碼是有限的,通過尋找td.t來判斷系統是否爬過頭了
        for link in soup.select('td.t  a.t'):  # 這里的td.t a.t 是點擊某個分類后的新網頁的每個具體商品的鏈接的selector
        #for link in soup.select( ('td.t a.t') if not soup.find_all('zhiding', 'huishou') else None ): #修改失敗,計劃排除被抓取的幾排廣告
            # 注意!!!上面代碼后面,若是('td.t >a.t')即無法顯示結果,必須空格!這樣才對('td.t > a.t')

            item_link = link.get('href').split('?')[0] # 這里的0是對切片后的字符串形成的列表list進行篩選,選第一段,即0(for in 就是對列表的)
            url_list.insert_one({'url': item_link })  # insert是數據庫函數,注意區分
            print(item_link)
    else:
        pass
#get_links_from('http://bj.58.com/shuma/', 2)

# spider 2 爬詳情頁的數據
def get_item_info(url):
    wb_data = requests.get(url)
    soup = BeautifulSoup(wb_data.text, 'lxml')
    no_longer_exist = '商品已下架' in soup.find('div', "button_li").get_text() # 從下方 AAA 處移過來的代碼,理解時先忽略它。
    # find()里面的代碼實際是完整的div="button_li",而且要保證該段代碼在正常網頁和已下架網頁中都存在,否則正常網頁報錯。
    if no_longer_exist:
        pass
    else:
        title = soup.title.text
        price = soup.select('span.price_now i')[0].text
        # 后面必須加[0].text,因為數據庫要是str才能存進去,soup.select返回的對象是list,就算list里面只有一個元素,也不能用.text方法,所以才選擇用[0],把元素從list調出來,再進行.text方法
        area = soup.select('.palce_li i')[0].text if soup.find_all('i') else None
        item_info.insert_one({'title':title, 'price':price, 'area':area })
        print({'title': title, 'price': price, 'area':area})

#get_item_info('http://zhuanzhuan.58.com/detail/919823388320399372z.shtml')

#======= AAA 爬取的商品鏈接中有失效的,剔除它(商品已交易則該網址會失效),測試完該段代碼備注掉==========#
# url = 'http://zhuanzhuan.58.com/detail/922439089107222541z.shtml'  # 網址上的商品已下架
# wb_data = requests.get(url)
# soup = BeautifulSoup(wb_data.text, 'lxml')
#print(soup.prettify())

# 上面的步驟查詢了失效網址的結構。
#no_longer_exist = '商品已下架' in soup.find('span', "soldout_btn").get_text()  # 搬到上方get_item_info
#print (no_longer_exist) # 查看no_longer_exist是True False。上面的find里代碼必須是完整的<xxx>內容,形成一個list,否則系統報錯屬性錯誤或者無法迭代

注意事項均備注在代碼中。。。

6. 多進程數據抓取

建立主程序 main.py

from multiprocessing import Pool #
from channel_extract import channel_list
from page_parsing import get_links_from

def get_all_links_from(channel):
    for num in range(1,51):
        get_links_from(channel,num)

if __name__=='__main__':   # 一種類似作文開頭的感謝領導的套話格式,防止上下程序串混亂了,沒特別的意思
    pool = Pool()  # 創建進程池
    pool.map(get_all_links_from, channel_list.split())
    # map函數的特點是把括號內的后一個參數放到前一個參數(函數)里去依次執行。約定俗成map第一個參數為不帶 () 的函數。
    # channel_list 是引用過來的,我們之前定義過它是一個長字符串,將它分成一段段,split()函數會將一個字符串自動變成分割好的一個大list

監控程序 counts.py

import time
from page_parsing import url_list # url_list 是數據庫的第一張表的名稱

while True:
    print(url_list.find().count())
    # find()展示url_list中所有的元素,count()計數,這兩種函數是數據庫函數,不能用于字典和列表
    time.sleep(5)
# 該段程序用來監控用,當它和主程序一起開的時候,它可以計算數量進程,方便管理

7. 運行

打開終端,開啟3個窗口,切換到程序文件夾中,第一個窗口輸入mongod,輸入mongo,好了,mongo已開啟
第二個窗口輸入 python3 counts.py
第三個窗口輸入python3 main.py
好了,開始抓取數據了,成功

8. 斷點續傳

from page_parsing import get_links_from, get_item_info, url_list, item_info    # 該條為更改的,下面代碼全部是新建的

# 斷點續傳
    db_urls = [ item['url'] for item in url_list.find() ]  # 用列表解析式裝入所要爬取的鏈接
    index_urls = [ item['url'] for item in item_info.find() ]   # 所引出詳情信息數據庫中所有的現存的 url 字段
    x = set(db_urls)     # 轉換成集合的數據結構
    y = set(index_urls)
    rest_of_urls = x - y

設計思路:

  1. 分兩個數據庫,第一個用于只用于存放抓取下來的 url (ulr_list);第二個則儲存 url 對應的物品詳情信息(item_info)
  2. 在抓取過程中在第二個數據庫中寫入數據的同時,新增一個字段(key)'index_url' 即該詳情對應的鏈接
  3. 若抓取中斷,在第二個存放詳情頁信息的數據庫中的 url 字段應該是第一個數據庫中 url 集合的子集
  4. 兩個集合的 url 相減得出圣賢應該抓取的 url 還有哪些


備注

(1) find()的參數依次為(標簽名,標簽屬性),返回一個標簽(可多重嵌套)或None;

(2)find_all()的參數依次為(標簽名,標簽屬性),返回一個標簽列表或者空列表
(3) python的find()是字符串對象的方法,用于查找子字符串,返回第一個字串出現的位置或-1(字串不存在);mongodb的find()是列表對象的方法,接收字典參數,鍵值對為所要查找條目鍵值對,用于查找條目,返回True
(4) mongodb的查詢方法find()find_one()
find()方法成功找到符合條件的記錄則返回一個生成器(實質是停留在符合條件記錄的集合的第一條記錄位置的cursor),用list方法轉化為列表后,如果該存在符合條件的記錄,則生成一個列表,否則生成一個空列表。

find_one({查詢鍵值對},{顯示字段:0表示不顯示or1表示顯示,其余默認不顯示,'_id'默認顯示})返回查詢到的第一條。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容