1. 程式人生 > >python爬蟲實戰(四):selenium爬蟲抓取阿里巴巴採購批發商品

python爬蟲實戰(四):selenium爬蟲抓取阿里巴巴採購批發商品

一、前言

二、學習資料(感謝分享)

三、開始爬取

1、先分析目標網址,為什麼選擇selenium

在搜尋中輸入女裝,用F12檢視原始碼,看看網頁顯示的內容是不是Ajax。點選Network,選擇下面的XHR,按F5重新整理頁面,下滑瀏覽器商品頁面
這裡寫圖片描述
只有兩個Ajax請求,而且Preview裡面並沒有程式碼。瞬間感覺天上掉餡餅了,難道是靜態網頁沒有其他資料請求嗎?如果是這樣直接弄個pyquery解析網頁就是了
這裡寫圖片描述

然而事實並不是這樣,下滑的時候都能感覺到,靜態網頁是一次性請求完畢,而這個下滑的時候,明顯有新的資料請求,於是再看了一下JS中,果然有資料
這裡寫圖片描述
如果只是用單純的requests.get()是得不到非同步請求的資料

2、selenium自動化測試,可見即可爬

from selenium import webdriver

browser=webdriver.Chrome()
def crawle():
    url='https://www.1688.com/'
    browser.get(url=url)

crawle()

執行後就產生了上圖,這個彈出來的框我們要叉掉,可以選擇點選“訪問1688首頁”。為了定位“訪問1688首頁”這個元素,右鍵檢查,可檢視元素
這裡寫圖片描述

from selenium import webdriver

browser=webdriver.Chrome()
def
crawle():
url='https://www.1688.com/' browser.get(url=url) button=browser.find_element_by_class_name('identity-cancel') button.click() crawle()

可以發現上圖的框沒見了(selenium中元素的定位很重要,有很多,開篇學習資料selenium中有詳細講解)

接下來需要在搜尋框中輸入我們想查詢的資料,如女裝,然後點選搜尋。我們需要做的就是

1、定位搜素框

2、向搜素框中傳入資料

3、定位搜素按鈕,並點選

from
selenium import webdriver browser=webdriver.Chrome() def crawle(): url='https://www.1688.com/' browser.get(url=url) #叉掉首頁彈出大框 button=browser.find_element_by_class_name('identity-cancel') button.click() #定位搜尋框 input=browser.find_element_by_id('alisearch-keywords') input.send_keys('女裝') #定位搜尋按鈕 sea_button=browser.find_element_by_id('alisearch-submit') sea_button.click() crawle()

執行後會跳轉頁面,如下圖,新的頁面中又有框圖,繼續叉掉它
這裡寫圖片描述

button_1=browser.find_element_by_class_name('s-overlay-close-l')
button_1.click()

接下來我想按照成交量排序,所以定位到成交量,並單擊這個元素

#定位成交量
button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
button_deal.click()

第一個函式:

from selenium import webdriver

browser=webdriver.Chrome()
def crawle():
    url='https://www.1688.com/'
    browser.get(url=url)
    #叉掉首頁彈出大框
    button=browser.find_element_by_class_name('identity-cancel')
    button.click()
    #定位搜尋框
    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys('女裝')
    #定位搜尋按鈕
    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    #叉掉框圖
    button_1=browser.find_element_by_class_name('s-overlay-close-l')
    button_1.click()
    #定位成交量
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()




crawle()

3、頁面如願以償地出來了,接下來就該抓取網頁的元素了

在crawl()最後一行呼叫函式get_products()

接下來就在get_products()裡面利用pyquery分析網頁,抓取網頁元素

可以發現每一個商品資訊都在ul下面的li中,我們需要定位到li,然後迴圈訪問每一個li元素。下面主要抓取產品名稱title,產品銷量deal,產品價格price,產品網址url

from selenium import webdriver

browser=webdriver.Chrome()
def crawle():
    url='https://www.1688.com/'
    browser.get(url=url)
    #叉掉首頁彈出大框
    button=browser.find_element_by_class_name('identity-cancel')
    button.click()
    #定位搜尋框
    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys('女裝')
    #定位搜尋按鈕
    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    #叉掉框圖
    button_1=browser.find_element_by_class_name('s-overlay-close-l')
    button_1.click()
    #定位成交量
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()
    get_products()


from pyquery import PyQuery as pq
from bs4 import BeautifulSoup   
def get_products():
    html=browser.page_source
    doc=pq(html)
    items=doc('.sm-offer .fd-clr .sm-offer-item').items()
    index=0
    for item in items:
        index+=1
        print('*'*50)
        title=item.find('.s-widget-offershopwindowtitle').text().split('\n')
        title=' '.join(title)
        price_a=item.find('.s-widget-offershopwindowprice').text().split('\n')
        price=''.join(price_a[:2])
        deal=''.join(price_a[2:])
        #產品網址
        text=item.find('.s-widget-offershopwindowtitle')
        soup=BeautifulSoup(str(text),'lxml')
        a=soup.select('.s-widget-offershopwindowtitle a')[0]
        url=a['href']
        print(title)
        print(price)
        print(deal)
        print(url)


    print(' (●ˇ∀ˇ●) '*5)
    print('一共%d條資料'%index)


crawle()

這個主要就是pyquery的運用

執行後結果如下:
這裡寫圖片描述
  問題來了,明顯這個頁面中不止20條資料。我們可以自己從首頁中搜索女裝,再點選成交量,慢慢向下滑動頁面,會察覺到有些資料是隨著滑動頁面產生新的資料。這說明selenium在載入網頁時,並沒有完全載入完返回。selenium中的顯式等待和隱式等待可以解決這個問題。

  顯式等待:讓webdriver等待滿足一定條件以後再進一步執行

  隱式等待:讓webdriver等待一定時間後查詢元素

  我們選擇顯式等待,並且希望能夠滾動頁面到最底部從而使所有資料加載出來。先手動滑到頁面的底端,可以發現每個li中有一個id,最後的一個id是offer60,可以發現一共就是60個店鋪。
  這裡寫圖片描述

顯示等待,wait會等待id為offer60的元素在15s內返回,否則報錯

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
browser=webdriver.Chrome()
wait=WebDriverWait(browser,15)


wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))

載入至頁面底端:

browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')

修改後:

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
browser=webdriver.Chrome()
wait=WebDriverWait(browser,15)

def crawle():
    url='https://www.1688.com/'
    browser.get(url=url)
    #叉掉首頁彈出大框
    button=browser.find_element_by_class_name('identity-cancel')
    button.click()
    #定位搜尋框
    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys('女裝')
    #定位搜尋按鈕
    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    #叉掉框圖
    button_1=browser.find_element_by_class_name('s-overlay-close-l')
    button_1.click()
    #定位成交量
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()
    try:
        browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    get_products()

from pyquery import PyQuery as pq
from bs4 import BeautifulSoup   
def get_products():
    html=browser.page_source
    doc=pq(html)
    items=doc('.sm-offer .fd-clr .sm-offer-item').items()
    index=0
    for item in items:
        index+=1
        print('*'*50)
        title=item.find('.s-widget-offershopwindowtitle').text().split('\n')
        title=' '.join(title)
        price_a=item.find('.s-widget-offershopwindowprice').text().split('\n')
        price=''.join(price_a[:2])
        deal=''.join(price_a[2:])
        #產品網址
        text=item.find('.s-widget-offershopwindowtitle')
        soup=BeautifulSoup(str(text),'lxml')
        a=soup.select('.s-widget-offershopwindowtitle a')[0]
        url=a['href']
        print(title)
        print(price)
        print(deal)
        print(url)


    print(' (●ˇ∀ˇ●) '*5)
    print('一共%d條資料'%index)


crawle()

執行後:
這裡寫圖片描述

  到處為止,我們已經做到了用selenium自動化開啟網頁,搜尋我們想查詢的資料,然後根據成交量排序抓取資料。

  接下來,還有幾點可以改善下:

  1、我們想自己在cmd中輸入我想查詢的類別,比如女裝,男裝,內衣等

  2、我們不止想抓取第一頁的資料,想自己定義抓取好多頁的資料

  3、實現把資料存入到mongodb中

先實現1和2,通過定義main()

def main():
    key_words=input('請輸入想查詢的類別:')
    page=int(input('你想查詢多少頁的資料:'))
    for key in key_words:
        crawle(key,page)

在crawle()中就需要新增引數key和page

def crawle(key,page):
    url='https://www.1688.com/'
    browser.get(url=url)
    button=browser.find_element_by_class_name('identity-cancel')
    button.click()
    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys(key)
    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    button_1=browser.find_element_by_class_name('s-overlay-close-l')
    button_1.click()
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()
    try:

        browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)
    if page>1:
        for page in range(2,page+1):
            get_more_page(key,page)

當page>1時,我們執行get_more_page(),這個函式會在下圖框中輸入對應頁數,然後跳轉,並呼叫get_products()抓取資料
這裡寫圖片描述

def get_more_page(key,page):
    page_input=browser.find_element_by_class_name('fui-paging-input')
    page_input.clear()
    page_input.send_keys(page)
    button=browser.find_element_by_class_name('fui-paging-btn')
    button.click()
    try:

        browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)

需要注意的是,我們在最後兩行實現了get_products()和save_to_mongo(), get_products()要想實現這個迴圈需要在get_products()中新增yield 請求

yield{
        'title':title,
        'deal':deal,
        'price':price,
        'url':url}

現在,實先儲存到mongodb中,用save_to_mongo()

import pymongo
client=pymongo.MongoClient()
db=client.alibaba
def save_to_mongo(item,key):
    #根據關鍵字動態存入相應的表
    collection=db[key]
    if item:
        collection.insert(item)
        print('成功儲存到mongo')

這個item也就是get_products()裡面的yield產生的資料
整體程式碼:

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from pyquery import PyQuery as pq
from bs4 import BeautifulSoup
browser=webdriver.Chrome()
wait=WebDriverWait(browser,15)

def crawle(key,page):
    url='https://www.1688.com/'
    browser.get(url=url)
    button=browser.find_element_by_class_name('identity-cancel')
    button.click()
    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys(key)
    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    button_1=browser.find_element_by_class_name('s-overlay-close-l')
    button_1.click()
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()
    try:
        browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)
    if page>1:
        for page in range(2,page+1):
            get_more_page(key,page)

def get_more_page(key,page):
    page_input=browser.find_element_by_class_name('fui-paging-input')
    page_input.clear()
    page_input.send_keys(page)
    button=browser.find_element_by_class_name('fui-paging-btn')
    button.click()

    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    try:


        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)


def get_products():
    html=browser.page_source
    doc=pq(html)
    items=doc('.sm-offer .fd-clr .sm-offer-item').items()
    index=0
    for item in items:
        index+=1
        print('*'*50)
        title=item.find('.s-widget-offershopwindowtitle').text().split('\n')
        title=' '.join(title)
        price_a=item.find('.s-widget-offershopwindowprice').text().split('\n')
        price=''.join(price_a[:2])
        deal=''.join(price_a[2:])
        #產品網址
        text=item.find('.s-widget-offershopwindowtitle')
        soup=BeautifulSoup(str(text),'lxml')
        a=soup.select('.s-widget-offershopwindowtitle a')[0]
        url=a['href']
        print(title)
        print(price)
        print(deal)
        print(url)
        yield{
        'title':title,
        'deal':deal,
        'price':price,
        'url':url}

    print(' (●ˇ∀ˇ●) '*5)
    print('一共%d條資料'%index)

import pymongo
client=pymongo.MongoClient()
db=client.alibaba
def save_to_mongo(item,key):
    #根據關鍵字動態存入相應的表
    collection=db[key]
    if item:
        collection.insert(item)
        print('成功儲存到mongo') 
def main():
    key_words=input('請輸入想查詢的類別:').split(' ')
    page=int(input('你想查詢多少頁的資料:'))
    for key in key_words:
        crawle(key,page)

main()

最後執行就可以看到資料庫中的資料如圖
這裡寫圖片描述
  咋看之下感覺沒有什麼問題,但是如果我們試著把資料的頁數改為兩頁,仔細觀察控制檯輸出的資訊就會發現兩次打印出的資料都是一樣的,都是第一頁網頁的資訊,這樣肯定不行
  解決辦法:要想解決問題,首先得知道問題出在哪裡,各位小夥伴要是自己有興趣,可以嘗試著去解決,下面我寫出我的建議,除錯程式碼,觀察程式碼執行時,chrome瀏覽器確實是跳轉到了第二頁,看我們的get_more_page()函式,裡面有一個execute_script(),它會將網頁滾動到最低

browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')

  然而我們可以發現chrome瀏覽器在跳轉到第二頁的時候並沒有將網頁滑動到最底端,但是又沒有報錯,這說明上面這段程式碼執行了的,但是第二頁確實沒有自動滑動底端,輸出的資料也確實是第一頁的,所以很有可能是瀏覽器響應的速度比程式碼執行的速度快
  什麼意思呢?我們的get_more_page()是為了跳轉到其它頁面,在執行

    button=browser.find_element_by_class_name('fui-paging-btn')
    button.click()

時候,瀏覽器確實跳轉到了指定的頁面,但是browser沒有及時的響應到新的頁面,還停留在上一個頁面,這個時候執行execute_script(),執行get_products()都是在對上一個網頁進行操作,所以我們打印出來的也是上一個頁面的資料,我們只需要讓程式緩幾秒,它便能夠緩過來,加入time.sleep(3)

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from pyquery import PyQuery as pq
from bs4 import BeautifulSoup
import time
browser=webdriver.Chrome()
wait=WebDriverWait(browser,15)

def crawle(key,page):
    url='https://www.1688.com/'
    browser.get(url=url)
    button=browser.find_element_by_class_name('identity-cancel')
    button.click()
    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys(key)
    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    button_1=browser.find_element_by_class_name('s-overlay-close-l')
    button_1.click()
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()
    try:
        browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)
    if page>1:
        for page in range(2,page+1):
            get_more_page(key,page)

def get_more_page(key,page):
    page_input=browser.find_element_by_class_name('fui-paging-input')
    page_input.clear()
    page_input.send_keys(page)
    button=browser.find_element_by_class_name('fui-paging-btn')
    button.click()
    time.sleep(3)
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    try:
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)


def get_products():
    html=browser.page_source
    doc=pq(html)
    items=doc('.sm-offer .fd-clr .sm-offer-item').items()
    index=0
    for item in items:
        index+=1
        print('*'*50)
        title=item.find('.s-widget-offershopwindowtitle').text().split('\n')
        title=' '.join(title)
        price_a=item.find('.s-widget-offershopwindowprice').text().split('\n')
        price=''.join(price_a[:2])
        deal=''.join(price_a[2:])
        #產品網址
        text=item.find('.s-widget-offershopwindowtitle')
        soup=BeautifulSoup(str(text),'lxml')
        a=soup.select('.s-widget-offershopwindowtitle a')[0]
        url=a['href']
        print(title)
        print(price)
        print(deal)
        print(url)
        yield{
        'title':title,
        'deal':deal,
        'price':price,
        'url':url}

    print(' (●ˇ∀ˇ●) '*5)
    print('一共%d條資料'%index)

import pymongo
client=pymongo.MongoClient()
db=client.alibaba
def save_to_mongo(item,key):
    #根據關鍵字動態存入相應的表
    collection=db[key]
    if item:
        collection.insert(item)
        print('成功儲存到mongo') 
def main():
    key_words=input('請輸入想查詢的類別:').split(' ')
    page=int(input('你想查詢多少頁的資料:'))
    for key in key_words:
        crawle(key,page)

main()

  這個時候再執行發現輸出時不再是重複的內容。

  然後,還是沒有結束,我們的main()執行時,key_words是個列表,用空格隔開,但是我們再輸入一種以上的類別時,便報錯了
這裡寫圖片描述
  顯示找不到元素,哪個元素呢?通過錯誤提示可以看出是找不到下圖中的“訪問1688首頁”這個元素,chrome在執行的時候,我們可以觀察到只有第一次搜尋的時候才會出現下圖,以後不會出現,我們只需在執行的時候加入try…except
這裡寫圖片描述

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from pyquery import PyQuery as pq
from bs4 import BeautifulSoup
import time
browser=webdriver.Chrome()
wait=WebDriverWait(browser,15)

def crawle(key,page):
    url='https://www.1688.com/'
    browser.get(url=url)
    try:
        button=browser.find_element_by_class_name('identity-cancel')
        button.click()
    except:
        pass

    input=browser.find_element_by_id('alisearch-keywords')
    input.send_keys(key)

    sea_button=browser.find_element_by_id('alisearch-submit')
    sea_button.click()
    try:
        button_1=browser.find_element_by_class_name('s-overlay-close-l')
        button_1.click()
    except:
        pass
    button_deal=browser.find_elements_by_css_selector('.sm-widget-sort.fd-clr.s-widget-sortfilt li')[1]
    button_deal.click()
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    try:

        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)
    if page>1:
        for page in range(2,page+1):
            get_more_page(key,page)

def get_more_page(key,page):
    page_input=browser.find_element_by_class_name('fui-paging-input')
    page_input.clear()
    page_input.send_keys(page)
    button=browser.find_element_by_class_name('fui-paging-btn')
    button.click()
    time.sleep(3)
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    try:
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#offer60')))
    except :
        print('*'*30,'超時載入','*'*30,'\n\n\n')
    for item in get_products():
        save_to_mongo(item,key)


def get_products():
    html=browser.page_source
    doc=pq(html)
    items=doc('.sm-offer .fd-clr .sm-offer-item').items()
    index=0
    for item in items:
        index+=1
        print('*'*50)
        title=item.find('.s-widget-offershopwindowtitle').text().split('\n')
        title=' '.join(title)
        price_a=item.find('.s-widget-offershopwindowprice').text().split('\n')
        price=''.join(price_a[:2])
        deal=''.join(price_a[2:])
        #產品網址
        text=item.find('.s-widget-offershopwindowtitle')
        soup=BeautifulSoup(str(text),'lxml')
        a=soup.select('.s-widget-offershopwindowtitle a')[0]
        url=a['href']
        print(title)
        print(price)
        print(deal)
        print(url)
        yield{
        'title':title,
        'deal':deal,
        'price':price,
        'url':url}

    print(' (●ˇ∀ˇ●) '*5)
    print('一共%d條資料'%index)

import pymongo
client=pymongo.MongoClient()
db=client.alibaba
def save_to_mongo(item,key):
    #根據關鍵字動態存入相應的表
    collection=db[key]
    if item:
        collection.insert(item)
        print('成功儲存到mongo') 
def main():
    key_words=input('請輸入想查詢的類別(ps:):').split(' ')
    page=int(input('你想查詢多少頁的資料:'))
    for key in key_words:
        time.sleep(3)
        crawle(key,page)

main()