【Scrapy學習心得】爬蟲實戰四(動態加載的頁面數據獲取)

【Scrapy學習心得】爬蟲實戰四(動態加載的頁面數據獲取)

聲明:僅供技術交流,請勿用於非法用途,如有其它非法用途造成損失,和本博客無關

爬取的網站:今日頭條各個板塊的新聞信息 點擊跳轉

本次爬蟲使用的是:scrapy+selenium

一、配置環境

  • python3.7
  • pycharm
  • Scrapy1.7.3
  • win10
  • pymysql

二、準備工作

  • cmd命令行中進入需要創建項目的目錄運行scrapy startproject haha
  • 創建成功後繼續執行cd haha
  • 然後執行scrapy genspider tt toutiao.com
  • 最後在spider文件夾下可以看到剛創建的tt.py爬蟲文件

三、分析網頁


可以在頁面的左邊找到對應板塊的url,並且會發現,頁面是通過AJAX加載頁面的,就是鼠標滑輪往下滾動,頁面內容纔會加載新的出來,所以此時如果用requests來爬的話,返回的信息肯定是不多的,那麼簡單粗暴的方法就是用selenium來爬了,那麼scrapy怎麼使用selenium呢,很簡單,只需添加一箇中間件就行了

所以總共我要爬取的內容有:

  • 分類的名稱以及其具體的url地址
  • 新聞的標題以及對應的url地址

查找元素的那些操作我就不放上來了,因爲沒什麼難度的,會來學scrapy框架的同學肯定是跟我一樣那些什麼requests啊,urllib啊,selenium啊等等都是用膩了纔來的,是吧

四、爬取數據

下面首先放上最重要的middlewares.py的代碼:

from scrapy import signals
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from scrapy.http.response.html import HtmlResponse

class seleniumDownloaderMiddleware(object):
    def __init__(self):
        options = webdriver.ChromeOptions()
        # options.add_argument('disable-infobars') #去除警告
        options.add_argument('headless') #無頭模式
        self.driver = webdriver.Chrome(options=options)
        # self.driver.maximize_window()
        self.num = 10  #需要滑滾頁面的次數,想拿多一點數據就設置的大一點

    def process_request(self,request,spider):
    	#第一次請求只需拿到各個板塊的url
        if request.url == 'https://www.toutiao.com/':
            self.driver.get(request.url)
            time.sleep(1)
            html = self.driver.page_source
            response = HtmlResponse(
                url=self.driver.current_url,
                body=html,
                request=request,
                encoding="utf-8"
            )
            return response
        else:
            self.driver.get(request.url)
            for i in range(self.num):
                ActionChains(self.driver).send_keys([Keys.END, Keys.PAGE_UP]).perform()
                time.sleep(1)
            html = self.driver.page_source
            response = HtmlResponse(
                url=self.driver.current_url,
                body=html,
                request=request,
                encoding="utf-8"
            )
            return response

下面是爬蟲文件tt.py的代碼如下:

# -*- coding: utf-8 -*-
import scrapy

class TtSpider(scrapy.Spider):
    name = 'tt'
    allowed_domains = ['toutiao.com']
    start_urls = ['https://www.toutiao.com/']

    def parse(self, response):
        li_list = response.xpath('//div[@class="channel"]/ul/li')
        for li in li_list:
            item = {}
            url = li.xpath('./a/@href').get()
            url = response.urljoin(url)
            name = li.xpath('.//span/text()').get()
            if name not in ['推薦', '西瓜視頻', '直播', '圖片', '懂車帝', '更多']:
                item['url']=url
                item['name']=name
                yield scrapy.Request(item['url'],callback=self.parse_news,meta={'item':item})

        li_list1 = response.xpath('//ul[@class="bui-box"]/li')
        for li in li_list1:
            item={}
            url = li.xpath('./a/@href').get()
            url = response.urljoin(url)
            name = li.xpath('.//span/text()').get()
            item['url'] = url
            item['name'] = name
            yield scrapy.Request(item['url'], callback=self.parse_news, meta={'item': item})

    def parse_news(self,response):
        item=response.meta['item']
        li_list = response.xpath('//div[@class="wcommonFeed"]/ul/li')
        for li in li_list:
            if li.xpath('.//a[@class="link title"]/text()').get() is not None:
                title = li.xpath('.//a[@class="link title"]/text()').get()
                title_url = li.xpath('.//a[@class="link title"]/@href').get()
                title_url = response.urljoin(title_url)
                item['title']=title
                item['title_url']=title_url
                yield item

五、保存數據

直接保存到pymysql數據庫當中,修改pipeline.py文件,代碼如下:

import pymysql

class HahaPipeline(object):
    def __init__(self):
        dbparams = {
            'host': 'localhost',
            'user': 'youruser',
            'password': 'yourpassword',
            'database': 'yourdatabase',
            'charset': 'utf8'
        }
        self.conn=pymysql.connect(**dbparams)
        self.cursor=self.conn.cursor()
        self.sql='insert into tt(name,title,title_url) values(%s,%s,%s)'

    def process_item(self, item, spider):
        self.cursor.execute(self.sql, [item['name'], item['title'], item['title_url']])
        self.conn.commit()

    def close_spider(self,spider):
        self.cursor.close()
        self.conn.close()

現在我們的爬蟲大致已經是寫完了,不過我還要修改一下setting.py文件的一些設置,需要增加的語句有:

LOG_LEVEL='WARNING' #設置日誌輸出級別
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'  #設置請求頭
ROBOTSTXT_OBEY = False  #把這個設置成False,就不會去請求網頁的robots.txt,因爲不改爲False的話,scrapy就會去訪問該網站的robots.txt協議,如果網站沒有這個協議,那麼它就不會去訪問該網站,就會跳過,進而爬不到數據
ITEM_PIPELINES = {
   'haha.pipelines.HahaPipeline': 300,
}
DOWNLOADER_MIDDLEWARES = {
   'haha.middlewares.seleniumDownloaderMiddleware': 543,
}

最後在cmd中先進入到這個項目的根目錄下,即有scrapy.cfg文件的目錄下,然後輸入並運行scrapy crawl tt,最後靜靜等待就行了

六、數據展示

經過幾分鐘後,數據庫裏面會出現以下數據:

寫在最後

這次主要是想在scrapy中實現selenium來處理請求返回動態加載的網頁數據,進而在scrapy中完美爬取動態頁面,不過,你會發現它是等所有的網頁請求完畢之後纔會有輸出,這個問題待解決,不過只要代碼寫正確了,也不影響哈哈,除非網絡斷了那數據就不能及時保存,爬蟲就白跑了,關鍵一點這裏不能用異步保存數據,會保存有重複的數據目前由於時間的原因,我沒有再去探究這個問題,希望有大佬給我指點指點

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章