Pages - Menu

2020年4月23日 星期四

[Scrapy]爬蟲除錯記錄

前言

最近才發現某個販賣二手書的查詢,跟實際上的入庫的書有出入。
最新更新有,但查詢沒有。
所以只好開始抓全部所有的新書,再做篩選了。
下面是碰到問題的解決方式

正文

1.擷取json資料

當網址讀出來是一段json的話,可使用json.load抓取資料,
記得 引入json

import json
result = json.loads(response.body_as_unicode())

但這個是一階的方式,
如果總共有兩層(Fig. 1)的話,則要再指定欄位

(Fig. 1)

item = items.usedBooksItem()
result = json.loads(response.body_as_unicode())
books = result["result1"]
for book in books:
    item['title'] = book["titleMain"]
    yield  item

2.允許過濾相同的URL

在查問題的過程中,發現了這段字 filtered duplicate request
然後才知道scrapy預設會過濾相同的url,所以要再request上面再加上 dont_filter = True
故整段request是這樣的

yield scrapy.Request(source_url,callback = self.parse, dont_filter = True)

不過scrapy其實也沒錯,你都抓過一次同樣網址的資料了,為什麼還要抓第二次。
後來才發現原因,我使用string.format 的方法組URL

source_url = "  http://www.xxxxx?startNum={0">http://www.xxxxx?startNum={0}&endNum={1}"
source_url = source_url .format(“0”,”1”)

當字串取代掉的同時,本來的source_url也就沒了 {0} 跟{1}
才會導致抓的都是同一個網址。

 

3.python的三元表達式

常用C#的人應該還蠻習慣用 ? : 的方式來取代if else,
因為夠簡潔(前提是邏輯判斷沒太複雜),一看就知道值
到了python就要改一下了。

True if condition else False

條件為True的話,結果放前面,else後面接False的程式
請注意,三元表達式不適合拿來寫太長太複雜的條件判斷

4.IndentationError: unindent does not match any outer indentation level

在寫items.py的時候一直跳這個錯誤出來,一般都是因為空白的關係。
但我怎麼刪都沒有用,後來才發現,
我不應該用Notepad++去刪除空格,我應該用Atom去刪。
原因在於,我有針對atom去做python的開發環境(可參考之前寫的文章,ATOM初學Python 環境設定)。

5.錯誤 Forbidden by robots.txt

更改settting.py文件 ,另外可以加上瀏覽器抬頭

ROBOTSTXT_OBEY = False
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'

6.將資料透過pipelines存入資料庫

當你解析完網站內容後,
要怎麼將資料存到資料庫?由於scrapy都幫你整理好了,所以不用你額外去呼叫。
但是~!!你必須有回傳值,告訴程式,你已經執行完成了。
像我下面的這段parse,解析完成後,使用yield item ,告訴start_request 執行完了,
你可以繼續執行下一步,如此才會啟動pipeline執行新增的動作(當然pipelines.py 需要自己寫一下)。
當然你也可以通通寫在主程式內,沒人阻止你。

def start_requests(self):
    base_url = "https://xxxxxxum={0}&endNum={1}&sortType=1"
    for page in range(1, 3):
        startNum = (page-1)* 46 + 1
        endNum =  page * 46
        source_url = base_url.format(str(startNum),str(endNum))
        yield scrapy.Request(source_url,callback = self.parse, dont_filter = True)
#查詢關鍵字
def parse(self, response):
    item = items.usedBooksItem()
    result = json.loads(response.body_as_unicode())
     books = result["result1"]
    for book in books:
        item['title'] = book["titleMain"]
        yield  item

另外,請注意return 與 yield 的不同。
Ref.
 Scraping a JSON response with Scrapy
Scrapy:抓取返回數據格式為JSON的網站內容
Convert JSON array to Python list
python中 ? : 三元表達式的使用介紹
Python腳本運行出現語法錯誤:IndentationError: unindent does not match any outer indentation level
爬蟲出現Forbidden by robots.txt

沒有留言:

張貼留言