爬蟲實(shí)戰(zhàn)(三)之 Scrapy 抓取博客類信息

經(jīng)過前面兩章簡書做基礎(chǔ),今天我們爬取相對(duì)難一點(diǎn)的項(xiàng)目-爬取用戶的博文相關(guān)信息-博文標(biāo)題、標(biāo)題鏈接、點(diǎn)擊數(shù)、評(píng)論數(shù)等;說到這里,有很多同學(xué)都做不住了:前面不是也是獲取過網(wǎng)頁的信息么,這些信息不是和前面一個(gè)樣;其實(shí),有這個(gè)想法是不錯(cuò)的,剛開始我也有這個(gè)想法,但是當(dāng)我真正去爬取的時(shí)候才發(fā)現(xiàn),“點(diǎn)擊數(shù)“ 和 “評(píng)論數(shù)” 是通過 js 動(dòng)態(tài)加載的,而博文標(biāo)題和博文鏈接是靜態(tài)的,因此這個(gè)實(shí)戰(zhàn)同時(shí)涉及到 靜態(tài) 和 動(dòng)態(tài) 網(wǎng)頁數(shù)據(jù)的獲取。
廢話不多說,接下來我們開搞,首先看下效果圖:
6.png
這里坑就坑在效果圖和顯示中是不一樣的,不過你怎么使用 xpath 表達(dá)式獲取“點(diǎn)擊數(shù)”和“評(píng)論數(shù)”就是只能得到默認(rèn)值,這讓人很是抓狂,大家可能有點(diǎn)不相信,那么我們就先用靜態(tài)的方式去獲取并輸出看看效果先
好,我們開始建立爬取博客項(xiàng)目:

scrapy startproject myblogproject(名字可自定義)

然后根據(jù)我們需要爬取的需求信息在 items.py 文件中定義爬取字段:
import scrapy

class MyblogprojectItem(scrapy.Item):
    # define the fields for your item here like:
    # 文章名
    name = scrapy.Field()
    # 文章地址
    url = scrapy.Field()
    # 文章閱讀數(shù)
    hits = scrapy.Field()
    # 文章評(píng)論數(shù)
    comment = scrapy.Field()

實(shí)體字段定義好之后我們創(chuàng)建一個(gè)基于 basic 版本的爬蟲文件:

scrapy genspider -t basic blogspider(爬蟲文件名可自定義) hexun.com

聰明的同學(xué)在編輯爬蟲文件之前會(huì)想到要去觀察下?lián)Q頁 url 的變化:
7.png
從圖中紅色方括號(hào)可以清晰的看到,第一個(gè)是用戶ID,第二個(gè)是頁碼
接下來開始編輯爬蟲文件:
# -*- coding: utf-8 -*-
import scrapy
from myblogproject.items import MyblogprojectItem
import re
import requests

class BlogspiderSpider(scrapy.Spider):
    name = 'blogspider'
    allowed_domains = ['hexun.com']

    offset = 1 #偏移量
    user_id = "toureiffel" #用戶id,要爬取不同用戶的中的文章數(shù)據(jù)換一下用戶ID即可
    # user_id = "xqw55555"
    # user_id = "tianyahaijiaoke"
    # user_id = "tjhyb2011"
    
    my_url = 'http://'+ user_id +'.blog.hexun.com/p1/default.html'
    start_urls = [my_url]

    def parse(self, response):
        item = MyblogprojectItem()

        node_list = response.xpath('//div[@class="Article"]')
        for node in node_list:
            item['name'] = node.xpath('./div/span/a/text()').extract_first()
            item['url'] = node.xpath('./div/span/a/@href').extract_first()
            #由于點(diǎn)擊數(shù)和評(píng)論數(shù)是動(dòng)態(tài)獲取的,因此不能直接在源碼中直接抓取
            item["hits"] = node.xpath('./div[2]/div[2]/span/text()').extract_first() # 0
            item["comment"] = node.xpath('./div[2]/div[2]/a[2]/span/text()').extract_first() # 0
            print(item['name'], item['url'], item["hits"], item["comment"])#輸出測試

        yield item


接下來一步就是在設(shè)置文件中做相應(yīng)的反屏蔽設(shè)置:
#以下幾個(gè)設(shè)置可以基本達(dá)到被反屏蔽的效果(當(dāng)然這只是簡單的幾種方式而已)
DEFAULT_REQUEST_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}

ROBOTSTXT_OBEY = False

COOKIES_ENABLED = False


我們接下來先測試一下運(yùn)行效果:
8.png
為何會(huì)出現(xiàn)如此奇葩的事情?觀察了好久,終究還是發(fā)現(xiàn)了那兩個(gè)玩意是動(dòng)態(tài)加載的,乖乖了。。。接下來我們一起去看下它到底是張啥樣的:
9.png
知道了這些數(shù)據(jù)的格式并且知道了這個(gè)鏈接,我們把鏈接復(fù)制一份在瀏覽器中打開發(fā)現(xiàn)其數(shù)據(jù)和剛才我們?cè)陂_發(fā)者模式看到的一模一樣,那么我們?nèi)绾问褂么a獲取呢???好,我們打開網(wǎng)頁的源代碼,然后將動(dòng)態(tài)鏈接復(fù)制過來查找一下,發(fā)現(xiàn)這個(gè)鏈接其實(shí)就在我們的源代碼中有:
10.png
好了,完事具備,就差我們?nèi)绾稳バ薷呐老x文件了:
# -*- coding: utf-8 -*-
import scrapy
from myblogproject.items import MyblogprojectItem
import re
import requests

class BlogspiderSpider(scrapy.Spider):
    name = 'blogspider'
    allowed_domains = ['hexun.com']

    offset = 1 #偏移量
    user_id = "toureiffel" #用戶id,要爬取不同用戶的中的文章數(shù)據(jù)換一下用戶ID即可
    # user_id = "xqw55555"
    # user_id = "tianyahaijiaoke"
    # user_id = "tjhyb2011"
    
    my_url = 'http://'+ user_id +'.blog.hexun.com/p1/default.html'
    start_urls = [my_url]


    def parse(self, response):
        item = MyblogprojectItem()

        #編寫獲取點(diǎn)擊數(shù)和評(píng)論數(shù) 鏈接 的正則表達(dá)式
        reg = r'<script type="text/javascript" src="(http://click.tool.hexun.com/.*?)">'
        myurl = re.compile(reg, re.S).findall(str(response.body)) #注:這里必先把字節(jié)轉(zhuǎn)換成字符串才能使用正則
        #print("提取的點(diǎn)贊和評(píng)論連接為-> "+ myurl[0]) #輸出測試
        headers = { "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36" }
        html = requests.get(myurl[0], headers=headers)#請(qǐng)求
        html.encoding = "utf-8" #編碼
        data = html.text #獲取源碼文本

        #提取點(diǎn)擊數(shù)的正則表達(dá)式
        hits_reg = r"click\d*','(\d*)'"
        hits_list = re.compile(hits_reg, re.S).findall(data)#獲取點(diǎn)擊數(shù)的列表
        #提取評(píng)論數(shù)的正則表達(dá)式
        common_reg = r"comment\d*','(\d*)'"
        common_list = re.compile(common_reg, re.S).findall(data)#獲取評(píng)論數(shù)的列表
        #賦值給變量
        item['hits'] = hits_list
        item['comment'] = common_list
        #使用 xpath 表達(dá)式獲取
        item['name'] = response.xpath('//div[@class="Article"]/div/span/a/text()').extract()
        item['url'] = response.xpath('//div[@class="Article"]/div/span/a/@href').extract()

        for i in range(0, len(item["name"])):
            #保存為csv文件
            blog_list = [item["name"][i], item["url"][i], item["hits"][i], item["comment"][i]]
            print("測試數(shù)據(jù)-> "+ str(blog_list))#輸出測試

        yield item


費(fèi)了那么大的力氣,接下來運(yùn)行測試一下:
11.png
這下終于出來了,哈哈哈,咱們一鼓做氣,把分頁頁搞定了,分頁爬取有兩種方式:一種是判斷“下一頁”按鈕,另一種就是拼接 url 地址(這兩種我們前面一個(gè)文章都詳細(xì)的分析過了,在這里我們使用拼接url方式,當(dāng)然如果使用拼接并且不用我們每次都去數(shù)論文的總數(shù)那才叫絕),修改代碼之前我們先看下源碼的截圖:
12.png
看到了吧,哈哈哈,總頁數(shù)是在源碼中可以獲取的,這樣我們就可以開始分頁的去爬取了,哈哈哈,在原來的爬蟲文件爬取一頁代碼的下面添加如下代碼即可:
        #獲取總頁數(shù)
        page_reg = r'blog.hexun.com/p(.*?)/'
        sum_page_list = re.compile(page_reg, re.S).findall(str(response.body))
        
        #如果用戶的文章只有一頁的話就無法得到總頁數(shù),因此我們必須寫個(gè)兼容這種情況的方法
        sum_page = 0 #存儲(chǔ)總頁數(shù)的變量
        if(len(sum_page_list) >= 2):
            sum_page = sum_page_list[-2]
        else:
            sum_page = 1 #如果只有一頁則賦值給1即可

        if self.offset < int(sum_page): #偏移量必須要小于總頁數(shù)
            self.offset += 1 #頁數(shù)記得自增
            print("-> 正在爬取第 %s 頁..."% str(self.offset))#輸出測試
            new_url = 'http://'+ self.user_id +'.blog.hexun.com/p'+ str(self.offset) +'/default.html'#拼接分頁的 url 
            yield scrapy.Request(new_url, callback = self.parse, headers=headers)#回調(diào)方法處理的是請(qǐng)求之后的數(shù)據(jù)


到這里我們?cè)龠\(yùn)行測試時(shí)發(fā)現(xiàn)可以正常全部數(shù)據(jù)數(shù)據(jù)(在這里不再截圖展示),然后我們要把我們剛獲取得的數(shù)據(jù)保存到本地文件中,那么我們就應(yīng)該在實(shí)體管道文件 pipelines.py 中處理數(shù)據(jù)了:
import csv

class MyblogprojectPipeline(object):

    def __init__(self):
        self.f = open("blogs.csv", "w")
        self.writer = csv.writer(self.f)
        self.writer.writerow(['博文標(biāo)題', '博文鏈接', '閱讀數(shù)', '評(píng)論數(shù)'])


    def process_item(self, item, spider):
        #循環(huán)寫入本地文件
        for i in range(0, len(item["name"])):
            #保存為csv文件
            blog_list = [item["name"][i], item["url"][i], item["hits"][i], item["comment"][i]]
            print(blog_list)#輸出測試
            self.writer.writerow(blog_list)

        return item


    def close_spider(self, spider):
        self.f.close()



這里就要注意了,要使管道文件有效,我們就必須在配置文件 settings.py 中將它開啟(這里值得一提的是管道配置文件的位置最好不要改變):
#啟動(dòng)管道
ITEM_PIPELINES = {
   'myblogproject.pipelines.MyblogprojectPipeline': 300,
}

好了,我們?cè)贉y試一次(這里為了清晰的看數(shù)據(jù)數(shù)據(jù),我把爬蟲文件的輸出測試語句給注釋掉了),然后開發(fā)本地文件:
13.png
到這里,我們就搞定一個(gè)項(xiàng)目了,哈哈哈,如果能幫助到您了,請(qǐng)給個(gè)贊吧!以激勵(lì)我接下來堅(jiān)持寫項(xiàng)目的動(dòng)力哦,^_^ (下一個(gè)項(xiàng)目將使用框架自動(dòng)收集圖片,敬請(qǐng)期待?。。?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,036評(píng)論 25 709
  • 1 前言 作為一名合格的數(shù)據(jù)分析師,其完整的技術(shù)知識(shí)體系必須貫穿數(shù)據(jù)獲取、數(shù)據(jù)存儲(chǔ)、數(shù)據(jù)提取、數(shù)據(jù)分析、數(shù)據(jù)挖掘、...
    whenif閱讀 18,313評(píng)論 45 523
  • 《從前慢》 作者:木心記得早先少年時(shí) 大家誠誠懇懇 說一句 是一句 清早上 火車站 長街黑暗無行人 賣豆?jié){的小店冒...
    漠北千紙鶴閱讀 231評(píng)論 0 0
  • 轉(zhuǎn)自 http://www.itdecent.cn/p/2b24cd430a7d 更新ubuntu軟件源 安裝...
    shaun_x閱讀 187評(píng)論 0 0
  • 帶著問題入場: 什么是JNI,NDK, 與Java是什么關(guān)系,有什么應(yīng)用場景? JNI提供了哪些基本數(shù)據(jù)類型? 接...
    csong閱讀 962評(píng)論 2 1

友情鏈接更多精彩內(nèi)容