2023年6月3日 星期六

[scrapy] 使用 scrapy 爬不到資料

Photo by ThisIsEngineering

記錄一下使用 scrapy 遇到的一些問題

問題:
用 browser 打開這個網頁明明就能看到畫面,但使用下面方法後
scrapy shell https://info.sogo.com.tw/tp1/floors/B2 
view(response)
卻只能看到下面錯誤

後來發現,這是因為少設定了 USER_AGENT 的關係,user agent 通常是用來識別 request 這方的軟體、os、device 等資訊,有些 server 會檢查這個 http header 資訊,若沒有的話,就不會回傳正確 response

處理方式:
在 settings.py 裡找到 USER_AGENT 設定,將一個正確的 user agent 填入即可,這邊我是參考 chrome 的 user agent 填入(如下)

問題:
網頁能打開後,卻找不到品牌列表,用 browser 打開往下 scroll 後,會看到如下的畫面,品牌列表的部份看起來是被放在這樣的結構裡

<div class="tab-content">
...
<a href="xxxx" class="brandBox">...</a>

但是使用
scrapy shell https://info.sogo.com.tw/tp1/floors/B2 
view(response)
卻只能看到 <div class="tab-content"> 裡沒有 brandBox 的資料

分析:
用 chrome 的 inspect 可以找到品牌列表是用這個 request 來的

而這個 request 是由 jquery-3.2.0.min.js 裡呼叫的
由此就能確定,找不到品牌列表是因為他是 javascript 的 request 而來的,scrapy 預設是爬靜態網頁,所以我邊需要針對這個 javascript 的 request 多做處理才行

這邊分享兩個 debug 技巧,
1. 在 spider 程式內呼叫 scrapy shell 的功能,在 def parse() 裡寫到下面即可
from scrapy.shell import inspect_response
inspect_response(response, self)
2. 在 spider 程式內印出 response 網頁資料,做法如下
def write_to_file(self, words):
    with open("logging.log", "a") as f:
        f.write(words)

def parse():
    self.write_to_file("response text: %s" % response.text)

處理方式:
使用 selenium 幫忙處理 javascript 的 request,照下面方法將 selenium 安裝且設定好,使用 selenium 的 function 做同一個 url 的 request,response 的資料裡就有品牌列表了
安裝 selenium
pip install scrapy-selenium
安裝 chrome driver
這裡下載跟自己使用的 chrome driver 相近的版本
在 settings.py 裡做好設定
總共四個設定,SELENIUM_DRIVER_EXECUTABLE_PATH 就是填上面下載的 chrome driver 檔案的路徑
在 spider 中使用
這邊不使用原本的 start_urls,而是使用如下的做法
from scrapy_selenium import SeleniumRequest
def start_request(self):
    target_url = "xxx"
    yield SeleniumRequest(url=target_url, callback=self.parse)

原本使用下面兩行去看有多少品牌是 0,設定成功之後,就可以看到 60
brands_num = len(response.xpath('//a[@class="brandBox"]'))
print("brands_num: " + (str)(brands_num))

Reference