在scrapy框架中使用selenium爬取强国论坛的新闻标题内容+redis增量式

本文仅供学习与交流,切勿用于非法用途!!!

第一部分(分析):

图1:

图2:

图3:

图4:

图5:

图6:

图7:

第二部分(实现代码):

实现爬取强国论坛的新闻标题内容,这里使用: scrapy startproject ProName > cd ProName >scrapy ganspider spiderName www.xxx.com 的scrapy工程和爬虫文件.

当然,也可以使用scrapy框架里面的CrawlSpider: scrapy startproject ProName > cd ProName > scrapy ganspider -t crawl spiderName www.xxx.com 实现爬取强国论坛的新闻标题内容.

1、创建好scrapy工程后,在配置文件settings.py里面设置USER_AGENT、日志级别和ROBOTSTXT_OBEY:

USER_AGENT = '自己设置User_Agent'
LOG_LEVEL = 'ERROR'##设置指定输出(报错的日志),减少CPU的使用率
ROBOTSTXT_OBEY = False #不遵从robots协议

****2、spiderName.py: ****

数据解析,获取到论坛新闻标题对应的url:

import scrapy

class PeopleSpider(scrapy.Spider):
    name = 'spiderName'
    # allowed_domains = ['www.xxx.com'] #把这个注释掉 用不到
    start_urls = ['http://bbs1.people.com.cn/board/1/1_1.html']#论坛首页url
    model_urls = []#动态加载数据的url

    def parse(self, response):
        li_list = response.xpath('/html/body/div[2]/div[3]/div[3]/ul/li')
        for li in li_list:
            detail_url = li.xpath('./p/a/@href').extract_first()#数据解析,获取到论坛新闻标题的url
            self.model_urls.append(detail_url)#由于标题内容是动态加载的,把其作用到全局

手动请求发送,获取到下一页论坛新闻标题对应的url:

import scrapy

class PeopleSpider(scrapy.Spider):
    name = 'spiderName'
    # allowed_domains = ['www.xxx.com'] #把这个注释掉 用不到
    start_urls = ['http://bbs1.people.com.cn/board/1/1_1.html']#论坛首页url
    url = 'http://bbs1.people.com.cn/board/1/1_%d.html'#论坛下一页的通用url
    pageNum = 2 #定义下一页的变量 进行手动请求发送

    model_urls = []#动态加载数据的url

    def parse(self, response):
        li_list = response.xpath('/html/body/div[2]/div[3]/div[3]/ul/li')
        for li in li_list:
            detail_url = li.xpath('./p/a/@href').extract_first()#数据解析,获取到论坛新闻标题的url
            self.model_urls.append(detail_url)#由于标题内容是动态加载的,把其作用到全局,使用selenium 来获取标题内容

    
    if self.pageNum < 3:#结束递归的条件
       new_url = format(self.url % self.pageNum)#下一页对应的完整url
       self.pageNum += 1
       yield scrapy.Request(url=new_url, callback=self.parse)#对下一页对应的url进行请求发生(手动请求发送)

使用selenium和请求传参:

在 items.py 里面:

class spiderNameproItem(scrapy.Item):#接受item对象
    detail_url = scrapy.Field()

在 spiderName.py 里面:

import scrapy
from selenium import webdriver
from spiderName.items import spiderNameproItem


class PeopleSpider(scrapy.Spider):
    name = 'spiderName'
    # allowed_domains = ['www.xxx.com'] #把这个注释掉 用不到
    start_urls = ['http://bbs1.people.com.cn/board/1/1_1.html']#论坛首页url
    url = 'http://bbs1.people.com.cn/board/1/1_%d.html'#论坛下一页的通用url
    pageNum = 2 #定义下一页的变量 进行手动请求发送
    model_urls = []#动态加载数据的url
    bro = webdriver.Chrome(executable_path='自己chromedriver的路径')
    def parse(self, response):
        item = spiderNameproItem()#实例化一个item对象,并且将解析到的数据储存到该对象中
        li_list = response.xpath('/html/body/div[2]/div[3]/div[3]/ul/li')
        for li in li_list:
            detail_url = li.xpath('./p/a/@href').extract_first()#数据解析,获取到论坛新闻标题的url
            self.model_urls.append(detail_url)#由于标题内容是动态加载的,把其作用到全局

        for url in self.model_urls:
            item['detail_url'] = url
            yield scrapy.Request(url=url, callback=self.parse_model, meta={'item': item})#meta的作用是:可以将meta字典传递给callback


        if self.pageNum < 3:#结束递归的条件
           new_url = format(self.url % self.pageNum)#下一页对应的完整url
           self.pageNum += 1
           yield scrapy.Request(url=new_url, callback=self.parse)#对下一页对应的url进行请求发生(手动请求发送)


    def parse_model(self,response):
        item = response.meta['item']#接收传递过来的meta

        '标题内容的数据解析部分就不写了'
        yield item#提交给管道


    def closed(self,spider):
        self.bro.quit()#关闭selenium 

在 middlewares.py 中间件里面:

from scrapy.http import HtmlResponse #scrapy 封装好的响应类
from selenium.webdriver.support.wait import WebDriverWait

#在 middlewares.py 里面class spidernameDownloaderMiddleware:前面的class类代码都干掉 只留下下面的类的三个方法

class spidernameDownloaderMiddleware:

    def process_request(self, request, spider):

        return None 

    
    def process_response(self, request, response, spider):#拦截所有响应对象

       
        if request.url in spider.model_urls:  #将拦截到的所有的响应对象中指定的标题内容动态加载的model_urls响应对象找出
            bro = spider.bro
            bro.get(request.url) #这里的request.url表示的就是指定的不满足需求的响应对象url

            try:

                WebDriverWait(bro,10).until(lambda el:bro.find_element_by_xpath('//*[@class="article scrollFlag"]'))#使用显示等待 等待标题内容的数据加载

            except: # 处理bro发起请求时,有时因为某种原因使得页面一直处于加载状态,使得源代码一直不能返回的问题

                bro.execute_script('window.stop()')

            page_text = bro.page_source #捕获标题内容的数据

            return HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request) #这里返回一个新的返回对象,替换原来原来不满足需求的、旧的响应对象
        else:
            return response  #这里的response表示的就是不指定 满足需求的响应对象

    def process_exception(self, request, exception, spider):

        pass

中间件弄好后,回到配置文件settings.py里面把中间件、管道(要对数据进去持久化存储的话)开启:

DOWNLOADER_MIDDLEWARES = {
   'spiderName.middlewares.PeopleproDownloaderMiddleware': 543,
}

ITEM_PIPELINES = {
   'spiderName.pipelines.PeopleproPipeline': 300,
}

加上增量式的代码 spiderName.py的完整代码如下:

这里实现的增量式是基于Redis数据库实现

import scrapy
from redis import Redis
from selenium import webdriver
from spiderName.items import spiderNameproItem


class PeopleSpider(scrapy.Spider):
    name = 'spiderName'
    # allowed_domains = ['www.xxx.com'] #把这个注释掉 用不到
    start_urls = ['http://bbs1.people.com.cn/board/1/1_1.html']#论坛首页url
    url = 'http://bbs1.people.com.cn/board/1/1_%d.html'#论坛下一页的通用url
    pageNum = 2 #定义下一页的变量 进行手动请求发送
    model_urls = []#动态加载数据的url
    bro = webdriver.Chrome(executable_path='自己chromedriver的路径')
    conn = Redis(host='127.0.0.1',port=6379) # 数据库链接对象

    def parse(self, response):
        item = spiderNameproItem()#实例化一个item对象,并且将解析到的数据储存到该对象中
        li_list = response.xpath('/html/body/div[2]/div[3]/div[3]/ul/li')
        for li in li_list:
            detail_url = li.xpath('./p/a/@href').extract_first()#数据解析,获取到论坛新闻标题的url
            self.model_urls.append(detail_url)#由于标题内容是动态加载的,把其作用到全局

        for url in self.model_urls:
            ex = self.conn.sadd('people_url',url) #ex==1插入成功,ex==0插入失败
            if ex == 1:#表示url没有记录在redis数据库里面
                item['detail_url'] = url
                yield scrapy.Request(url=url, callback=self.parse_model, meta={'item': item})#meta的作用是:可以将meta字典传递给callback


        if self.pageNum < 3:#结束递归的条件
           new_url = format(self.url % self.pageNum)#下一页对应的完整url
           self.pageNum += 1
           yield scrapy.Request(url=new_url, callback=self.parse)#对下一页对应的url进行请求发生(手动请求发送)


    def parse_model(self,response):
        item = response.meta['item']#接收传递过来的meta

        '标题内容的数据解析部分就不写了'
        yield item#提交给管道


    def closed(self,spider):
        self.bro.quit()#关闭selenium 

这是开启redis数据库跑这个工程的效果图:

本文可以借鉴学习,切勿照搬,根据自己分析的实际情况实现项目!!!

posted @ 2021-02-20 16:52  我是容易  阅读(174)  评论(0编辑  收藏  举报