使用pyppeteer爬取淘宝商品
之前我用pyppeteer繞過了淘寶登錄時對于web driver的檢測,但是這并不意味這登錄
后就沒有檢測了,今天我就來以爬取搜索關鍵字得到的商品名稱為例操作一下。
整個過程有 4 步:1.登錄,2.輸入關鍵字并點擊搜索,3.滑到最底部并獲取數據,4.點擊下一頁,然后重復步驟 3,4 直到沒有下一頁(實際上一個賬號并不能每一頁全部爬完,要想全部爬完可能要買或者借賬號,下面的教程我只爬前幾頁的數據)。然后就是要搭好一個框架,為了讓程序看起來簡單,我就使用面向對象的設計方法來設計這個程序,大致代碼如下:
class TaoBaoSpider:async def login(self):passasync def search(self):passasync def crawl(self):pass因為pyppeteer是通過async和await異步機制來實現的,所以方法必須都是異步的,所以我在每個方法定義之前都加上了async關鍵字。
登錄
登錄有兩種方式,一種是手機掃碼登錄,另一種是輸入賬號密碼登錄,為了盡可能簡單和盡可能的不被檢測,我使用手機掃碼登錄。既然使用手機掃碼登錄,那么 login 方法的實現就非常簡單,只要 sleep 一會就行了。但這里需要注意的是,不能使用 time 模塊的 sleep 函數(這是同步阻塞接口的等待,不能在異步函數中出現),我們需要一個異步等待,這個異步等待就是 asyncio 模塊的 sleep 函數,調用時需要用關鍵字 await 修飾,如下所示。
from asyncio import sleepclass TaoBaoSpider:@staticmethodasync def login():await sleep(10)async def search(self):passasync def crawl(self):pass這里我設置等待時間為 10 秒,在這 10 秒鐘完成手機掃碼登錄應該夠了,如果覺得時間緊迫可以把 10 改成更大的數。
下面我們需要進行測試,在測試之前,我們要先寫好這個類的構造方法,來初始化一些屬性,因為在 search 和 crawl 方法中都需要使用瀏覽器對象,所以我們要初始化一個瀏覽器對象,如下所示。
self.browser = await launch(headless=False, args=['--disable-infobars', f'--window-size={self.width},{self.height}'])
我們都知道,await 關鍵字必須在 async 修飾的函數體內,有些人可能會想到按照下面這種寫法:
async def __init__(self)
這樣寫是錯的,如圖所示。
稍微翻譯一下,說函數“__init__”不可以是異步的!那么我們就需要單獨定義一個異步方法來完成異步的初始化。這個異步方法我就定義為 init 了。下面就主要編寫 __init__ 和 init 兩個方法。
from asyncio import sleep, get_event_loop from pyppeteer import launchclass TaoBaoSpider:def __init__(self):self.width, self.height = 1500, 800get_event_loop().run_until_complete(self.init())get_event_loop().run_until_complete(self.login())get_event_loop().run_until_complete(self.search())get_event_loop().run_until_complete(self.crawl())async def init(self):# noinspection PyAttributeOutsideInitself.browser = await launch(headless=False, args=['--disable-infobars', f'--window-size={self.width},{self.height}'])# noinspection PyAttributeOutsideInitself.page = await self.browser.newPage()await self.page.setViewport({'width': self.width, 'height': self.height})await self.page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')await self.page.evaluate('()=>{Object.defineProperties(navigator,{webdriver:{get:()=>false}})}')@staticmethodasync def login():await sleep(10)async def search(self):passasync def crawl(self):pass運行程序會彈出一個Chromium瀏覽器,瀏覽器顯示的就是淘寶登錄頁面,手機掃碼可以成功登錄,沒有被檢測,登錄部分測試完畢,下面我們主要編寫一下search方法。
?
搜索
在編寫搜索方法之前,我們來想一個問題,剛登錄就搜索是不是太快了?我們都知道,訪問太快容易被 BAN 的,所以我們需要降低訪問速度。這個可以通過異步等待來實現。
下面又來了一個關鍵性的問題,我具體應該等待多久?時間太長效率就太低,時間太短容易被檢測,我們可以想一下自己手動訪問淘寶速度是怎樣的,我感覺是1到4秒進行一次點擊啥的,有些人就想當然認為
這就太簡單了,直接構造方法中初始化不就完了嗎?如下所示:
self.sleep_time = 1+random()*3
其實這樣寫是錯的,因為這個字段值在中途不會被重新初始化,也就是每兩個事件之間都是相同的時間間隔,這樣不被檢測出來就有鬼,畢竟人操作不可能這么機械化,那么我們需要在每次調用它的時候都能修改它的值,既然這個值在不斷的變化,我們可以把它定義成方法,可是定義成方法后面的每次調用都要一對括號,這簡直是太繁瑣了。我們可以使用 property 來裝飾這個方法,在調用的時候不需要加括號了,下面我實現搜索的方法。
from asyncio import sleep, get_event_loop from pyppeteer import launch from random import randomclass TaoBaoSpider:def __init__(self):self.width, self.height = 1500, 800get_event_loop().run_until_complete(self.init())get_event_loop().run_until_complete(self.login())get_event_loop().run_until_complete(self.search())get_event_loop().run_until_complete(self.crawl())async def init(self):# noinspection PyAttributeOutsideInitself.browser = await launch(headless=False,args=['--disable-infobars', f'--window-size={self.width},{self.height}'])# noinspection PyAttributeOutsideInitself.page = await self.browser.newPage()await self.page.setViewport({'width': self.width, 'height': self.height})await self.page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')await self.page.evaluate('()=>{Object.defineProperties(navigator,{webdriver:{get:()=>false}})}')@staticmethodasync def login():await sleep(10)@propertydef sleep_time(self):return 1+random()*3async def login():await sleep(10)@propertydef sleep_time(self):return 1+random()*3async def search(self):await self.page.click('#q')await sleep(self.sleep_time)await self.page.keyboard.type('機械革命')await sleep(self.sleep_time)await self.page.click('#J_TSearchForm > div.search-button > button')await sleep(self.sleep_time)async def crawl(self):passif __name__ == '__main__':TaoBaoSpider()在這里我搜索的關鍵字直接賦了初值,大家可以自己修改成輸入傳參的形式,這很簡單,我就不講了,下面是最關鍵的步驟,爬取數據。
爬取數據
爬取數據我們就爬一下搜索到的商品名稱,下圖中價格下面的兩行字。
通過審查元素我們可以找到每一段字的對應的HTML的源碼形式,如下圖所示:
既然a標簽下還有標簽,那么我們就需要做出二次過濾,第一次提取a標簽下的內容,第二次把第一次提取的數據的標簽和空白字符去掉。所以需要些兩個正則,首先是提取a標簽下的內容,經過不停地比對,可以得出最后的正則:
pattern = compile(r'<a id=".*?" class="J_ClickStat".*?>(.*?)</a>',S)
接著是用來替換的正則:?
repl_pattern = compile(r'<.*?>|\s+')
接下來我們嘗試獲取前5頁的數據。先到底部(不能一蹴而就,一定要模擬人的操作。這里使用勻加速來實現,加速度是一個隨機值,不隨機可能會被檢測),然后獲取頁面源碼進行數據篩選。最后跳到下一頁,重復之前的操作。下面直接給出完整的源代碼。
from asyncio import sleep, get_event_loop from pyppeteer import launch from random import random from re import compile, Sclass TaoBaoSpider:def __init__(self):self.width, self.height = 1500, 800get_event_loop().run_until_complete(self.init())get_event_loop().run_until_complete(self.login())get_event_loop().run_until_complete(self.search())get_event_loop().run_until_complete(self.crawl())async def init(self):# noinspection PyAttributeOutsideInitself.browser = await launch(headless=False,args=['--disable-infobars', f'--window-size={self.width},{self.height}'])# noinspection PyAttributeOutsideInitself.page = await self.browser.newPage()await self.page.setViewport({'width': self.width, 'height': self.height})await self.page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')await self.page.evaluate('()=>{Object.defineProperties(navigator,{webdriver:{get:()=>false}})}')@staticmethodasync def login():await sleep(10)@propertydef sleep_time(self):return 1+random()*3async def search(self):await self.page.click('#q')await sleep(self.sleep_time)await self.page.keyboard.type('機械革命')await sleep(self.sleep_time)await self.page.click('#J_TSearchForm > div.search-button > button')await sleep(self.sleep_time)async def crawl(self):pattern = compile(r'<a id=".*?" class="J_ClickStat".*?>(.*?)</a>', S)repl_pattern = compile(r'<.*?>|\s+')for i in range(5):# document.body.clientHeightheight = await self.page.evaluate('document.body.clientHeight')scrolled_height = 0a = 1+random()t = 1# window.scrollTo(width, height)while scrolled_height < height:scrolled_height = int(1/2*a*t**2) # x=v0*t+1/2*a*t**2,v0=0await self.page.evaluate(f'window.scrollTo(0,{scrolled_height})')t += 1await sleep(self.sleep_time)html = await self.page.content()results = pattern.findall(html)for result in results:result = repl_pattern.sub('', result)print(result)print()await sleep(self.sleep_time)await self.page.click('#mainsrp-pager > div > div > div > ul > li.item.next > a')await sleep(self.sleep_time)await sleep(self.sleep_time)if __name__ == '__main__':TaoBaoSpider()運行結果如圖所示。
可以發現顯示在網頁上的數據被爬了下來,下面來總結一些應付這種反爬特別嚴的網站的一些技巧:
1.模擬人的操作,每次請求都要隨機等待一會。
2.如果需要登錄,盡量手動登錄,自動登錄可能會被檢測。
3.一個賬號(如果要登錄)或一個IP訪問次數不要太多,如果要爬很多數據可以使用多個賬號(如果要登錄)或者對個IP。
總結
以上是生活随笔為你收集整理的使用pyppeteer爬取淘宝商品的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python爬虫神器pyppeteer
- 下一篇: C库函数 - strcspn()