zoukankan      html  css  js  c++  java
  • 在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数据库跑这个工程的效果图:

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

  • 相关阅读:
    李宏毅 Keras手写数字集识别(优化篇)
    李宏毅 Keras2.0演示
    李宏毅 线性回归预测PM2.5
    李宏毅 Gradient Descent Demo 代码讲解
    Pandas导入导出&pickle文件模块
    python(29)Tinker+BeautifulSoup+Request抓取美女壁纸
    golang(11) 反射用法详解
    golang(10)interface应用和复习
    golang(09) golang 接口内部实现
    golang(08)接口介绍
  • 原文地址:https://www.cnblogs.com/YYQ-4414/p/14421554.html
Copyright © 2011-2022 走看看