技術科普

知乎數據爬取

2018-05-15 13:53:19 45

命令行執行scrapy shell  獲取知乎內容

scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36" https://www.zhihu.com/question/39808733

這里特別注意(我們都知道知乎必須每次都發送USER_AGENT,才能訪問),所以這里必須帶上USER_AGENT,否則知乎返回的是500錯誤或者400錯誤,正確代碼如上。

這樣我們看到返回200頁面

image.png

實戰:

for url in self.start_urls:
    yield scrapy.Request(url, dont_filter=True, headers=self.headers)

我們上面的代碼是沒有回調函數callback的,所有獲取到后直接跳轉到parse函數進行處理

def parse(self, response):
    """
    提取出html頁面中的所有url 并跟蹤這些url進行一步爬取
    如果提取的url中格式為 /question/xxx 就下載之后直接進入解析函數
    """
    # 提取頁面所有url
    all_urls = response.css("a::attr(href)").extract()
    all_urls = [parse.urljoin(response.url,url) for url in all_urls]

這里我們提取到的url是question/39808733,不是我們想要的完整的地址,這個時候可以使用一個函數urljoin來自動拼接我們的url,他會自動將我們提取到的url跟提取頁面也就是response.url進行自動拼接。

all_urls = [parse.urljoin(response.url,url) for url in all_urls]

當然它是在urllib里的,兼容寫法為:

try:
    import urlparse as parse
except:
    from urllib import parse
def parse(self, response):
        """
        提取出html頁面中的所有url 并跟蹤這些url進行一步爬取
        如果提取的url中格式為 /question/xxx 就下載之后直接進入解析函數
        """
        all_urls = response.css("a::attr(href)").extract()
        all_urls = [parse.urljoin(response.url, url) for url in all_urls]
        all_urls = filter(lambda x:True if x.startswith("https") else False, all_urls)
        for url in all_urls:
            match_obj = re.match("(.*zhihu.com/question/(\d+))(/|$).*", url)
            if match_obj:
                #如果提取到question相關的頁面則下載后交由提取函數進行提取
                request_url = match_obj.group(1)
                yield scrapy.Request(request_url, headers=self.headers, callback=self.parse_question)
            else:
                #如果不是question頁面則直接進一步跟蹤
                yield scrapy.Request(url, headers=self.headers, callback=self.parse)

以上代碼說明:

all_urls里包括可能不是https的鏈接,所以需要過濾,這里我們使用filter函數,lambda函數自己百度使用方法,:我們對all_urls里的每個url也就是x進行過濾,當滿足是https時保留,不是時返回False。

這里還可以直接用if判斷:

for url in all_urls:
    if url.startswith("https"):
    下面是邏輯...


以上部分代碼省略,接下來直接進行知乎問題處理:

def parse_question(self, response):
    # 處理question頁面, 從頁面中提取出具體的question item
if "QuestionHeader-title" in response.text:
        # 處理新版本
match_obj = re.match("(.*zhihu.com/question/(\d+))(/|$).*", response.url)
        if match_obj:
            question_id = int(match_obj.group(2))

        item_loader = ItemLoader(item=ZhihuQuestionItem(), response=response)
        item_loader.add_css("title", "h1.QuestionHeader-title::text")
        item_loader.add_css("content", ".QuestionHeader-detail")
        item_loader.add_value("url", response.url)
        item_loader.add_value("zhihu_id", question_id)
        item_loader.add_css("answer_num", ".List-headerText span::text")
        item_loader.add_css("comments_num", ".QuestionHeader-actions button::text")
        item_loader.add_css("watch_user_num", ".NumberBoard-value::text")
        item_loader.add_css("topics", ".QuestionHeader-topics .Popover div::text")

        question_item = item_loader.load_item()
    else:
        # 處理老版本頁面的item提取
match_obj = re.match("(.*zhihu.com/question/(\d+))(/|$).*", response.url)
        if match_obj:
            question_id = int(match_obj.group(2))

        item_loader = ItemLoader(item=ZhihuQuestionItem(), response=response)
        # item_loader.add_css("title", ".zh-question-title h2 a::text")
item_loader.add_xpath("title",
                              "//*[@id='zh-question-title']/h2/a/text()|//*[@id='zh-question-title']/h2/span/text()")
        item_loader.add_css("content", "#zh-question-detail")
        item_loader.add_value("url", response.url)
        item_loader.add_value("zhihu_id", question_id)
        item_loader.add_css("answer_num", "#zh-question-answer-num::text")
        item_loader.add_css("comments_num", "#zh-question-meta-wrap a[name='addcomment']::text")
        # item_loader.add_css("watch_user_num", "#zh-question-side-header-wrap::text")
item_loader.add_xpath("watch_user_num",
                              "//*[@id='zh-question-side-header-wrap']/text()|//*[@class='zh-question-followers-sidebar']/div/a/strong/text()")
        item_loader.add_css("topics", ".zm-tag-editor-labels a::text")

        question_item = item_loader.load_item()

    yield scrapy.Request(self.start_answer_url.format(question_id, 20, 0), headers=self.headers,
                         callback=self.parse_answer)
    yield question_item

說明:

ItemLoader需要引入:(同時需要引入知乎的兩個Item)

from scrapy.loader import ItemLoader
from items import ZhihuQuestionItem,ZhihuAnswerItem

item_loader = ItemLoader(item=ZhihuQuestionItem(), response=response)

這個位置傳遞的是知乎的Item和返回的response


江苏11选5分析网站