問題描述:在爬取一些反爬機制做的比較好的網站時,經常會遇見一個問題就網站代碼是通過js寫的,這種就無法直接使用一般的爬蟲工具爬取,這種情況一般有兩種解決方案
第一種:把js代碼轉為html代碼,然后再使用html代碼解析工具爬取,目前常用的工具是selenium和scrapy-splash,我使用的是第一個工具,第二個還有搞個docker服務,太麻煩
第二種:自己觀察js代碼,找到存放數據的地方,直接獲取,這種方式需要有js基礎,反正我看到一堆亂七八糟的js就頭大,這種方式pass
下面就是第一種實現方式:
技術架構:scrapy-redis + selenium + webdriver
解釋:使用scrapy-redis進行分布式爬蟲效率高,而且直接把url放到redis中,這種方式對於請求鏈接的管理非常簡單, selenium這工具可以直接融入到scrapy中,作為一個中間件,至於這個中間件的原理,網上有很多資料,其實原理很簡單,就是每次請求進來,先讓selenium這中間件處理一下,把js代碼轉為html,然后直接return一個對象給spider進行爬蟲,這個對象里面放的就是html,
下面就是這個中間件的代碼:
class SeleniumMiddleware(object): def __init__(self,timeout=25): profile = FirefoxProfile() profile.set_preference('permissions.default.image', 2) self.browser = webdriver.Firefox(profile) self.timeout = timeout self.browser.maximize_window() # # self.browser.implicitly_wait(20) self.browser.set_page_load_timeout(self.timeout) self.wait = WebDriverWait(self.browser, self.timeout) def __del__(self): self.browser.close() def process_request(self, request, spider): """ 用WebDriver抓取頁面 :param request: Request對象 :param spider: Spider對象 :return: HtmlResponse """ logging.info('******WebDriver is Starting******') try: #這里的ip和port可以根據自己的情況填充,比如通過api獲取的代理ip,或者從代理池中獲取也可以 ip = '60.182.17.174' port = '4224' #user_agent仍然可以動態修改,這里測試寫死,網上有很多每次請求隨機修改代理的user-agent的方法 user_agent ='Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)' self.browser.get("about:config") script = ''' var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); prefs.setIntPref("network.proxy.type", 1); prefs.setCharPref("network.proxy.http", "{ip}"); prefs.setIntPref("network.proxy.http_port", "{port}"); prefs.setCharPref("network.proxy.ssl", "{ip}"); prefs.setIntPref("network.proxy.ssl_port", "{port}"); prefs.setCharPref("network.proxy.ftp", "{ip}"); prefs.setIntPref("network.proxy.ftp_port", "{port}"); prefs.setBoolPref("general.useragent.site_specific_overrides",true); prefs.setBoolPref("general.useragent.updates.enabled",true); prefs.setCharPref("general.useragent.override","{user_agent}"); '''.format(ip = ip,port=port,user_agent=user_agent) self.browser.execute_script(script); time.sleep(1); self.browser.get(request.url) self.wait.until(EC.presence_of_element_located((By.XPATH, '//div[@class="s-result-list sg-row"]'))) return HtmlResponse(url=request.url, body=self.browser.page_source, request=request, encoding='utf-8', status=200) except TimeoutException: return HtmlResponse(url=request.url, status=500, request=request)
-------------------------------------------------姑娘滑溜溜的馬甲線------------------------------------------------------
注意:這是網上目前可以找到的唯一個完整代碼的解決方案,可以直接復制粘貼,上面都沒有說重點,其實這里最重要的就是動態修改代理ip,網上很多資料都是當瀏覽器啟動的時候指定代理ip,那如果想要更換代理ip,不好意思,重啟瀏覽器,這種方式效率非常低,對於一個有追求的程序員來說就是種恥辱
然后把這個中間件配置到settings中:
DOWNLOADER_MIDDLEWARES = { 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware' : None, # 必需 ,禁用默認的middleware 'amazon.custom_rewrite.SeleniumMiddlewares.SeleniumMiddleware': 541, #自定義selenium中間件 }
-------------------------------------------------姑娘滑溜溜的馬甲線------------------------------------------------------
更新:上面只是解決了動態代理ip的問題,那如何解決動態修改瀏覽器頭呢,很簡單,只需要在上面的js中添加
prefs.setBoolPref("general.useragent.site_specific_overrides",true); prefs.setBoolPref("general.useragent.updates.enabled",true); prefs.setCharPref("general.useragent.override","{user_agent}");
-------------------------------------------------姑娘滑溜溜的馬甲線------------------------------------------------------
2019-04-17更新:
上面的配置在運行的過程中,瀏覽器一般運行幾天之后就會崩潰, 我定位了很久才發現是瀏覽器內存泄露導致的,因為firefox瀏覽器默認是可以使用緩存的,隨着爬蟲的運行,這就會使瀏覽器的緩存越來越大,從而導致內存
泄露,那怎么解決呢?很簡單,直接把緩存給禁用了就可以,不過有的爬蟲需要用緩存加快爬蟲的速度,這種情況下我還沒有想到好的處理辦法,一個思路是定時啟動瀏覽器,比如定時5個小時重啟一次瀏覽器,但是這樣子有點麻煩吧,下面是禁用
緩存的代碼
prefs.setBoolPref("browser.cache.disk.enable", false); prefs.setBoolPref("browser.cache.memory.enable", false); prefs.setBoolPref("browser.cache.offline.enable", false);
說明:火狐瀏覽器從25版本之后就已經在about:config中無法找到general.useragent.override屬性了,解決辦法就是在about:config右鍵,新建-->字符串,添加這個屬性就可以