使用selenium和pyquery爬取京東商品列表過程解析
今天一起學(xué)起使用selenium和pyquery爬取京東的商品列表。本文的所有代碼是在pycharm IDE中完成的,操作系統(tǒng)window 10。
1、準(zhǔn)備工作
安裝pyquery和selenium類庫。依次點(diǎn)擊file->settings,會彈出如下的界面:
然后依次點(diǎn)擊:project->project Interpreter->"+",,如上圖的紅色框所示。然后會彈出下面的界面:
輸入selenium,在結(jié)果列表中選中“selenium”,點(diǎn)擊“install package”按鈕安裝selenium類庫。pyquery也是一樣的安裝方法。
安裝chrome和chrome driver插件。chrome dirver插件下載地址:http://npm.taobao.org/mirrors/chromedriver/。 切記chrome和chrome dirver的版本要一致。我的chrome版本是70,對應(yīng)chrome driver是2.44,2.43,2.42。
下載chrome driver解壓后,將exe文件拷貝到pycharm開發(fā)空間的Scripts文件夾中:
2、分析要爬取的頁面
這次是爬取京東圖書中計(jì)算機(jī)書籍類書籍的信息。
打開chrome,打開開發(fā)者工具,輸入www.jd.com,分析查詢輸入框和查詢按鈕的css代碼:
通過分析發(fā)現(xiàn),搜索框的css代碼是id=“key”,查詢按鈕的css代碼是class=“button”。下面是使用selenium調(diào)用chrome瀏覽器在搜索框輸入關(guān)鍵詞“計(jì)算機(jī)書籍”并點(diǎn)擊查詢按鈕出發(fā)查詢請求的代碼:
from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from pyquery import PyQuery as pq #通過Chrome()方法打開chrome瀏覽器 browser = webdriver.Chrome() #訪問京東網(wǎng)站 browser.get("https://www.jd.com") #等待50秒 wait = WebDriverWait(browser, 50) #通過css選擇器的id屬性獲得輸入框 input = browser.find_element_by_id('key') #在輸入框中寫入要查詢的信息 input.send_keys('計(jì)算機(jī)書籍') #獲取查詢按鈕 submit_button = browser.find_element_by_class_name('button') #點(diǎn)擊查詢按鈕 submit_button.click()
上面代碼成功啟動chrome瀏覽器,自動完成在搜索框中輸入關(guān)鍵詞并點(diǎn)擊查詢按鈕的操作。
點(diǎn)擊完查詢按鈕之后,會加載出符合條件的書籍,如下圖所示:
鼠標(biāo)往下滾動到達(dá)網(wǎng)頁底部時,會看到分頁的界面:
下一步要做的工作就是分析商品列表頁和分頁的css代碼。
我們要爬去圖書的書名、圖片、價格、出版社、評價數(shù)量信息。下圖是商品列表也的界面,
通過開發(fā)者工具可知class="gl-item"的li節(jié)點(diǎn)是一條商品的信息,上圖這個的紅色框。
- 綠色框是商品的圖片信息。對應(yīng)的是class=“p-img”的div節(jié)點(diǎn)。
- 藍(lán)色框是商品的價格信息,對應(yīng)的是class="p-price"的div節(jié)點(diǎn)。
- 黑色框是商品的名稱信息,對應(yīng)的是class="p-name"的div節(jié)點(diǎn)。
- 紫色狂是商品的評價信息,對應(yīng)的是class="p-commit"的div節(jié)點(diǎn)。
- 褐色框是商品的出版社信息,對應(yīng)的是class=“p-shopnum”的div節(jié)點(diǎn)。
我們使用pyquery解析商品的信息,使用selenium打開一個頁面時,通過page_source屬性就可以得到頁面的源碼。
這里有個坑需要注意:京東的商品列表頁是顯示固定數(shù)量的商品,當(dāng)加載新的商品頁的時候,并不是一下子把該頁的商品都加載出來,而是鼠標(biāo)向下滾動時才會動態(tài)加載新的商品。因此我們在使用selenium時,要將鼠標(biāo)設(shè)置自動滾動到商品列表頁的底部,這樣就會把該頁的所有商品都顯示出現(xiàn),爬取的數(shù)據(jù)才能完整,否則會出現(xiàn)丟失。
下面給出解析一個商品的代碼:
from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from pyquery import PyQuery as pq import time #通過Chrome()方法打開chrome瀏覽器 browser = webdriver.Chrome() #訪問京東網(wǎng)站 browser.get("https://www.jd.com") #等待50秒 wait = WebDriverWait(browser, 50) #通過css選擇器的id屬性獲得輸入框 input = browser.find_element_by_id('key') #在輸入框中寫入要查詢的信息 input.send_keys('計(jì)算機(jī)書籍') #獲取查詢按鈕 submit_button = browser.find_element_by_class_name('button') #點(diǎn)擊查詢按鈕 submit_button.click() # 模擬下滑到底部操作 for i in range(1, 5): browser.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(1) #商品列表的總頁數(shù) total = wait.until( EC.presence_of_all_elements_located( (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > em:nth-child(1) > b') ) ) html = browser.page_source.replace('xmlns', 'another_attr') doc = pq(html) #一個商品信息是存放在class=“gl-item”的li節(jié)點(diǎn)內(nèi),items()方法是獲取所有的商品列表。 li_list = doc('.gl-item').items() #循環(huán)解析每個商品的信息 for item in li_list: image_html = item('.gl-i-wrap .p-img') book_img_url = item.find('img').attr('data-lazy-img') if book_img_url == "done": book_img_url = item.find('img').attr('src') print('圖片地址:' + book_img_url) item('.p-name').find('font').remove() book_name = item('.p-name').find('em').text() print('書名:' + book_name) price = item('.p-price').find('em').text() + str(item('.p-price').find('i').text()) print('價格:' + price) commit = item('.p-commit').find('strong').text() print('評價數(shù)量:' + commit) shopnum = item('.p-shopnum').find('a').text() print('出版社:' + shopnum) print('++++++++++++++++++++++++++++++++++++++++++++')
對于有分頁的情況,需要一頁一頁的解析商品,我們可以通過selenium調(diào)用“下一頁”按鈕來獲取下一頁的源代碼。我們來分析下一頁的css代碼,滾動鼠標(biāo)到網(wǎng)頁的底部,會看到分頁的情況:
通過上圖可知,需要獲取到“下一頁”按鈕,然后調(diào)用click方法。相應(yīng)的代碼為:
next_page_button = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em')) ) next_page_button.click() #滑動到頁面底部,用于加載數(shù)據(jù) for i in range(0,3): browser.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(10) #一頁顯示60個商品,"#J_goodsList > ul > li:nth-child(60)確保60個商品都正常加載出來。 wait.until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#J_goodsList > ul > li:nth-child(60)")) ) # 判斷翻頁成功,當(dāng)?shù)撞康姆猪摻缑嫔巷@示第幾頁時,就顯示翻頁成功。 wait.until( EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), str(page_num)) )
下面給出完整代碼:
from selenium import webdriver from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from pyquery import PyQuery as pq import time #打開不同的瀏覽器實(shí)例 def openBrower(brower_type): if brower_type == 'chrome': return webdriver.Chrome() elif brower_type == 'firefox': return webdriver.Firefox() elif brower_type == 'safari': return webdriver.Safari() elif brower_type == 'PhantomJS': return webdriver.PhantomJS() else : return webdriver.Ie() def parse_website(): # 通過Chrome()方法打開chrome瀏覽器 browser = openBrower('chrome') # 訪問京東網(wǎng)站 browser.get("https://www.jd.com") # 等待50秒 wait = WebDriverWait(browser, 50) # 通過css選擇器的id屬性獲得輸入框。until方法表示瀏覽器完全加載到對應(yīng)的節(jié)點(diǎn),才返回相應(yīng)的對象。presence_of_all_elements_located是通過css選擇器加載節(jié)點(diǎn) input = wait.until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, '#key')) ) # input = browser.find_element_by_id('key') # 在輸入框中寫入要查詢的信息 input[0].send_keys('計(jì)算機(jī)書籍') # 查詢按鈕完全加載完畢,返回查詢按鈕對象 submit_button = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, '.button')) ) # 點(diǎn)擊查詢按鈕 submit_button.click() # 模擬下滑到底部操作 for i in range(0,3): browser.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(3) # 商品列表的總頁數(shù) total = wait.until( EC.presence_of_all_elements_located( (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > em:nth-child(1) > b') ) ) html = browser.page_source.replace('xmlns', 'another_attr') parse_book(1,html) for page_num in range(2,int(total[0].text) + 1): parse_next_page(page_num,browser,wait) ##解析下一頁 def parse_next_page(page_num,browser,wait): next_page_button = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em')) ) next_page_button.click() #滑動到頁面底部,用于加載數(shù)據(jù) for i in range(0,3): browser.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(10) #一頁顯示60個商品,"#J_goodsList > ul > li:nth-child(60)確保60個商品都正常加載出來。 wait.until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#J_goodsList > ul > li:nth-child(60)")) ) # 判斷翻頁成功,當(dāng)?shù)撞康姆猪摻缑嫔巷@示第幾頁時,就顯示翻頁成功。 wait.until( EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), str(page_num)) ) html = browser.page_source.replace('xmlns', 'another_attr') parse_book(page_num, html) def parse_book(page,html): doc = pq(html) li_list = doc('.gl-item').items() print('-------------------第' + str(page) + '頁的圖書信息---------------------') for item in li_list: image_html = item('.gl-i-wrap .p-img') book_img_url = item.find('img').attr('data-lazy-img') if book_img_url == "done": book_img_url = item.find('img').attr('src') print('圖片地址:' + book_img_url) item('.p-name').find('font').remove() book_name = item('.p-name').find('em').text() print('書名:' + book_name) price = item('.p-price').find('em').text() + str(item('.p-price').find('i').text()) print('價格:' + price) commit = item('.p-commit').find('strong').text() print('評價數(shù)量:' + commit) shopnum = item('.p-shopnum').find('a').text() print('出版社:' + shopnum) print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++') def main(): parse_website() if __name__ == "__main__": main()
3、總結(jié)
(1)要記得調(diào)用 browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")方法模擬鼠標(biāo)向下滾動的操作加載數(shù)據(jù),否則數(shù)據(jù)會不完整。
(2)在通過page_source獲取網(wǎng)頁源碼時,如果有xmlns命名空間,則要將該命名空間空其他的字段代替,否則使用pyquery解析網(wǎng)頁時,會解析不出數(shù)據(jù)。pyquery解析xmlns命名空間時,會自動隱藏掉某些屬性。導(dǎo)致無法征程解析網(wǎng)頁,原因不詳,如果有人知道原因請告知。
(3)盡量用wait.until(EC.presence_of_all_elements_located())方法,這樣可以避免網(wǎng)頁無法正常加載而提前返回網(wǎng)頁信息的情況。保證數(shù)據(jù)的準(zhǔn)確。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python實(shí)現(xiàn)定時精度可調(diào)節(jié)的定時器
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)定時精度可調(diào)節(jié)的定時器,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04Python著名游戲?qū)崙?zhàn)之方塊連接 我的世界
讀萬卷書不如行萬里路,學(xué)的扎不扎實(shí)要通過實(shí)戰(zhàn)才能看出來,本篇文章手把手帶你模仿著名游戲——我的世界,大家可以在過程中查缺補(bǔ)漏,看看自己掌握程度怎么樣2021-10-10六個Python編程最受用的內(nèi)置函數(shù)使用詳解
在日常的python編程中使用這幾個函數(shù)來簡化我們的編程工作,經(jīng)常使用能使編程效率大大地提高。本文為大家總結(jié)了六個Python編程最受用的內(nèi)置函數(shù),感興趣的可以了解一下2022-07-07Python利用itchat對微信中好友數(shù)據(jù)實(shí)現(xiàn)簡單分析的方法
Python 熱度一直很高,我感覺這就是得益于擁有大量的包資源,極大的方便了開發(fā)人員的需求。下面這篇文章主要給大家介紹了關(guān)于Python利用itchat實(shí)現(xiàn)對微信中好友數(shù)據(jù)進(jìn)行簡單分析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-11-11