构造分布式Scrapy_redis爬虫,爬取新浪新闻sina整站的新闻文章-day1

爬虫第一步:新建项目

- 选择合适的位置,执行命令:**scrapy startproje sinaNews**(sinaNews是自定义爬虫项目名称) 成功执行后,**如图**: ![](https://img2018.cnblogs.com/blog/1066684/202001/1066684-20200108174529820-1736211639.png)

爬虫第二步:明确目标

- 首先打开要爬取的网站:https://news.sina.com.cn/ 在**导航div(main-nav)**中发现有**15个父类**,图片中**颜色深的是父类标题** ![](https://img2018.cnblogs.com/blog/1066684/202001/1066684-20200108175325039-795261414.png) - 由于在15个父类中包含的不止三个子类,所以在**此页面取到父类的url**后发送Request请求,获得父类中**所有子类的url** 如:点进**父类:财经**后,有**如下**这些**子类标题** ![](https://img2018.cnblogs.com/blog/1066684/202001/1066684-20200108180310363-1183641528.png) - 所有子类点进去后,就可以找到每篇文章的url,拿到文章的url后,就可以文章的标题和内容了(由于有的子类加载数据的方式不同,本文中的方法可能不适用所有的子类页面结构,后续会慢慢更新和维护,在此先写一个公共的方法) - 分析后,明确了我们需要获取的**item字段**: **item["parentUrls"]**:文章所属父类的url **item["subUrls"]**:文章所属子类的url **item["newsUrl"]**:文章本身的url **item["newsTitle"]**:文章标题 **item["newsContent"]**:文章内容

用xpath在页面中提取我们需要的数据

  • item["parentUrls"]:在start_urls中获取,xpath规则://div[@class="main-nav"]//ul/li[1]/a/@href

    如图,多了两个错误的url,在代码中用切片方法切掉就可以了。

  • item["subUrls"]:在父类页面中获取,由于每个父类的页面结构都不同,所以需要不同的xpath规则来获取子类url
    如:在财经页面:子类url提取的xpath规则//div[@class="m-nav"]//a/@href

  • item["newsUrl"]:在子类页面中获取,由于文章的url都是有.html结束的,所以我采用的是在子类页面中获取所有的连接://li//a/@href,然后在代码中用endwith(.html)取出文章的url

  • item["newsTitle"]:在文章页面获取,大多数文章页面的文章标题提取规则都差不多,xpath规则://h1[@class="main-title"]

  • item["newsContent"]:在文章页面获取,xpath规则://div[@class="article"]//p

  • 确认完item字段后,编写items.py文件

import scrapy
class SinanewsItem(scrapy.Item):
    # 由于后期要做分布式爬取,所以多加了两个字段(crawled,spider),方便区分数据的来源和时间
    # 父类url
    parentUrls = scrapy.Field()
    # 子类url
    subUrls = scrapy.Field()
    # 每条新闻的url
    newsUrls = scrapy.Field()
    # 每条新闻的标题
    newsTitle = scrapy.Field()
    # 每条新闻的内容
    newsContent = scrapy.Field()
    # 爬取时间
    crawled = scrapy.Field()
    # 爬取数据的spider名 
    spider = scrapy.Field()

爬虫第三步:编写爬虫文件spider

- 本文使用spider类编写,进入sinaNews目录,执行命令:**scrapy genspider sina "sina.com.cn"** 其中**sina是爬虫名**,不能和爬虫项目名重复,"sina.cn"爬虫限制的**域名**,**限制爬虫的范围** - 执行成功后,在spider目录先会出现sina.py文件,打开他编写自己的爬虫程序 ~~~ import scrapy class SinaSpider(scrapy.Spider): name = 'sina' allowed_domains = ['sina.com'] start_urls = ['http://sina.com/'] # 此方法是spider类爬虫必须写的,用来解析第一个(start_urls)页面 def parse(self, response): pass ~~~ **写入自己的逻辑**:
import scrapy
# 从items.py文件中导入SinanewsItem类
from sinaNews.items import SinanewsItem

class SinaSpider(scrapy.Spider):
    name = 'sina'
    # 允许爬虫的范围
    allowed_domains = ['sina.com.cn']
    # 改为sina新闻的首页url
    start_urls = ['http://news.sina.com.cn/']

    # 此方法是自动调用的方法,用来解析start_urls页面结构
    def parse(self, response):
        # 创建一个空的items列表,为了数据的传递
        items = []
        # 所有大类的url 共17个 发现多了两个,用切片方法把它切掉
        parentUrls = response.xpath('//div[@class="main-nav"]//ul/li[1]/a/@href').extract()[:-2]
        # 遍历15个url
        for i in range(0,len(parentUrls)):
            # 实例化item一个item对象,用来保存字段数据
            item = SinanewsItem()
            # 保存父类url
            item["parentUrls"]=parentUrls[i]
            # 添加到items列表中
            items.append(item)
        # 遍历列表
        for item in items:
            # 将15个父类的请求Request交给scheduler调度器入队列,meta保存item信息传给回调函数,回调函数second_parse是用来处理父类请求的Response
            yield scrapy.Request(url=item["parentUrls"],meta={"meta_1":item},callback=self.second_parse)

    # 用来解析15父类请求返回的页面,由于每个页面的结构不同,所以需要写15个方法来处理(这里我用了一个比较笨的方法,因为初学,经验不足,但是我相信肯定有更好的方法,欢迎大家评论)
    def second_parse(self,response):
        items=[]
        # 拿到返回页面的url,根据url的不同,进行不同的处理方法
        if response.url == "https://news.sina.com.cn/":
            # 跟进上面分析的xpath规则,取到子类的url
            subUrls = response.xpath('//div[@class="cNav2"]//a/@href').extract()
            # 遍历解析到的子类url           
            for i in range(0,len(subUrls)):
                item = SinanewsItem()
                # 将数据存到item里面
                item["parentUrls"] = response.url
                item["subUrls"] = subUrls[i]
                items.append(item)
            for item in items:
                # 将子类url的请求再入到请求队列,meta传递数据,回调函数news_parse用来解析所有子类请求返回的页面
                yield scrapy.Request(url=item["subUrls"],meta={"meta_2":item},callback=self.news_parse)
        # 这里先写两个父类页面的解析,其实都差不多,只是提取子类url的xpath规则有所改变,其它的都一样
        elif response.url == 'https://finance.sina.com.cn/':
            subUrls = response.xpath('//div[@class="m-nav"]//a/@href').extract()
            for i in range(0,len(subUrls)):
                item = SinanewsItem()
                item["parentUrls"] = response.url
                item["subUrls"] = subUrls[i]
                items.append(item)
            for item in items:
                # 所有的子类页面都调用news_parse来处理结果
                yield  scrapy.Request(url=item["subUrls"],meta={"meta_2":item},callback=self.news_parse)
    # 这个方法是用来提取每个子类页面中的每一篇文章的url
    def news_parse(self,response):
        # 提取上个方法传递的item数据
        meta = response.meta["meta_2"]
        items = []
        # 提取页面中所有的url连接
        urls = response.xpath('//li//a/@href').extract()
        for url in urls:
            # 遍历提取以.html结尾的url
            if url.endswith(".html"):
                item = SinanewsItem()
                # 存储item数据
                item["newsUrls"] = url
                item["parentUrls"] = meta["parentUrls"]
                item["subUrls"] = meta["subUrls"]
                items.append(item)
            for item in items:
                # 再将所有文章的url请求入队列,回调函数gov_parse来处理文章页面
                yield scrapy.Request(url=item["newsUrls"],meta={"meta_3":item},callback=self.gov_parse)
    # 这个方法用来处理每个文章的url,因为文章页面的结构大多都差不多,所以写一个统一的方法,如果有不同的页面,也可以写不同的方法来处理
    def gov_parse(self,response):
        # 获取传递的数据
        meta = response.meta["meta_3"]
        # 实例化
        item = SinanewsItem()
        # 获取每一篇文章标题
        title = response.xpath('//h1[@class="main-title"]/text()').extract()
        # 获取每一篇文章类容
        content = response.xpath('//div[@class="article"]//p/text()').extract()
        # 判断是否有取到内容,以防程序报错
        if len(content) != 0 and len(title) != 0:
            item["parentUrls"] = meta["parentUrls"]
            item["subUrls"] = meta["subUrls"]
            item["newsUrls"] = meta["newsUrls"]
            item["newsTitle"] = title[0]
            # 由于取到的文章内容是一个列表,用join方法将列表转为字符串
            new_content = "\n".join(content)
            item["newsContent"] = new_content
            # 将取到的数据发给pipeline来处理
            yield item

爬虫第四步:存储内容,编写管道文件pipeline.py

-由于是是构造**分布式爬虫**,所以用的**scrapy_redis的管道**来处理,但是我们可以让数据先流过我们自己写的管道,所以可以**加两个字段**
from datetime import datetime
class SinanewsPipeline(object):
    # 当数据流过我们的管道时,加上crawled和spider
    def process_item(self, item, spider):
        # 格林威治时间,+8就是北京时间
        item["crawled"] = datetime.utcnow()
        # 存储当前爬虫的爬虫名
        item["spider"] = "snia"
        return item

到目前为止,爬取新浪新闻所有文章的代码差不多写完了,以上代码基本上拿过去就可以用(前提是sinau页面结构没有改动),但是让整个程序跑起来,还需要修改seeting.py文件

下一页

posted @ 2020-01-07 15:54  我要去巴黎  阅读(204)  评论(0)    收藏  举报