> 文章列表 > scrapy爬虫爬取多网页内容

scrapy爬虫爬取多网页内容

scrapy爬虫爬取多网页内容

摘要:此案例是爬取目标网站(https://tipdm.com/)的新闻中心板块的公司新闻中所有新闻的标题、发布时间、访问量和新闻的文本内容。

1. 创建scrapy项目

我使用的是 Anaconda prompt
我们使用如下命令创建scrapy项目:scrapy startproject spider_name 爬虫路径

spider_name是项目的名字
爬虫路径就是项目所在位置

本案例内命令是:scrapy startproject mySpider 路径

2. 生成爬虫器

我们先生成 spider(爬虫器) 文件:scrapy genspider spider_name domain

  1. spider_name:是生成的 爬虫器的名字,在目录spiders 下面
  2. domain:是要爬取的网站的域名(一般是不加http,只写www.xxx.com就行)

本案例内,命令是scrapy genspider msd www.tipdm.com

3. 指定爬取字段(items.py)

指定要爬取的内容都有什么,我们在摘要中已经说明,要爬取的是 所有新闻的标题、发布时间、访问量和新闻的文本内容
所以有四个字段,代码如下:

import scrapyclass MyspiderItem(scrapy.Item):title = scrapy.Field()time = scrapy.Field()view = scrapy.Field()text = scrapy.Field()

4. 编写爬虫文件(msd.py)

我们要爬取的是:所有页面的、所有新闻的内容。
所以我们写三个parse函数。

  1. parse:得到页数,并得到每页的网址,传给下个函数
  2. parse_url:得到每个页面的所有新闻的链接
  3. parse_text:分析得到的新闻界面的内容,并存入 item 中,返回回去

注意:将 start_url 修改成第一个要爬取的页面,即公司新闻的页面

(代码中注释掉的内容都是调试用的)

parse函数

先得到总页数,因为取出来的是 str,将其转成int类型。
通过取出各个页面可知,每个页面的网址类似,只有数字不同,我们就可以生成网址了。
然后使用 yield Request() 回调下一个函数。
现在还不太懂原理。yield ,并不会直接返回,而是会返回一个生成器,可以遍历完所有的网页再统一返回

    def parse(self, response):number = int(response.xpath('//*[@id="t251"]/div[6]/div/a[6]/text()').extract()[0]) # 总页数url_all = ['http://www.tipdm.com/gsxw/index_{}.jhtml'.format(i) for i in range(1, number + 1)]for i in url_all:yield Request(url=i, callback=self.parse_url, dont_filter=True)

parse_url

得到单个页面中的所有新闻的网址,放在一个列表中
注意:这里被卡了好久,就是因为得到的网址是不完整的,没有前缀(http之类的)一直进不到下一个函数(可以在每个函数写一个输出语句,以便检查是否进入了函数)
所以我们将url补充完毕,进行回调
爬取不出来的时候,一定要输出一下网址看看

    def parse_url(self, response):urls = response.xpath('//*[@id="t251"]/div/div[3]/h1/a/@href').extract()for i in urls:yield Request(url=('http://www.tipdm.com' + i), callback=self.parse_text, dont_filter=True)

parse_text

本函数是分析新闻的内容的函数,并存储在item中返回
有个很尴尬的点是这个网站的前端有点乱,有几条新闻的文本内容的xpath路径和其他的都不一样,好麻烦的,现在还不知道怎么解决

    def parse_text(self, response):item = MyspiderItem()item['title'] = response.xpath('/html/body/div[2]/div/div[1]/div[2]/h1/text()').extract()  # titleitem['time'] = response.xpath('/html/body/div[2]/div/div[1]/div[2]/div/div[1]/span[1]/text()').extract()  # timeitem['view'] = response.xpath('/html/body/div[2]/div/div[1]/div[2]/div/div[1]/span[3]/text()').extract()  # viewitem['text'] = '\\n'.join(response.xpath('/html/body/div[2]/div/div[2]/p/text()').extract())if item['text'] == '':item['text'] = '\\n'.join(response.xpath('/html/body/div[2]/div/div[2]/p/span/text()').extract())if item['text'] == '':item['text'] = '\\n'.join(response.xpath('/html/body/div[2]/div/div[2]/section[2]/section/section[2]/section[2]/p/span/text()').extract())return item

完整代码

import scrapy
from scrapy.http import Request
from mySpider.items import MyspiderItemclass MsdSpider(scrapy.Spider):name = 'msd'allowed_domains = ['www.tipdm.com']start_urls = ['http://www.tipdm.com/gsxw/index.jhtml']def parse(self, response):number = int(response.xpath('//*[@id="t251"]/div[6]/div/a[6]/text()').extract()[0]) # 总页数url_all = ['http://www.tipdm.com/gsxw/index_{}.jhtml'.format(i) for i in range(1, number + 1)]for i in url_all:# print('1111111111111111')yield Request(url=i, callback=self.parse_url, dont_filter=True)# print(i)def parse_url(self, response):urls = response.xpath('//*[@id="t251"]/div/div[3]/h1/a/@href').extract()# print('22222222222222222222')for i in urls:yield Request(url=('http://www.tipdm.com' + i), callback=self.parse_text, dont_filter=True)def parse_text(self, response):# print('77')item = MyspiderItem()# print('78')item['title'] = response.xpath('/html/body/div[2]/div/div[1]/div[2]/h1/text()').extract()  # title# print('7999')item['time'] = response.xpath('/html/body/div[2]/div/div[1]/div[2]/div/div[1]/span[1]/text()').extract()  # timeitem['view'] = response.xpath('/html/body/div[2]/div/div[1]/div[2]/div/div[1]/span[3]/text()').extract()  # viewitem['text'] = '\\n'.join(response.xpath('/html/body/div[2]/div/div[2]/p/text()').extract())if item['text'] == '':item['text'] = '\\n'.join(response.xpath('/html/body/div[2]/div/div[2]/p/span/text()').extract())if item['text'] == '':item['text'] = '\\n'.join(response.xpath('/html/body/div[2]/div/div[2]/section[2]/section/section[2]/section[2]/p/span/text()').extract())# print('79')return item

5. 存储数据(pipelines.py、settings.py)

我们选择使用pipelines存储,需要在settings文件中取消相应代码注释

settings.py

ITEM_PIPELINES = {'mySpider.pipelines.MyspiderPipeline': 300,
}

pipelines.py

因为我选择存储为.csv文件,所以需要导入pandas
注意to_csv中,参数顺序不能乱

其中,参数的顺序如下:

  1. filename: 文件名,必需。
  2. index: 是否写入行索引,可选默认为False
  3. encoding: 字符编码,可选。默认为utf-8-sig
  4. compression: 压缩模式,可选。默认为gzip
  5. header: 表头是否写入,可选。默认为None
  6. footer: 表尾是否写入,可选。默认为None
  7. newline: 换行符,可选。默认为None
  8. dtype: 数据类型,可选。默认为None
  9. optimizer: 优化器,可选。默认为None
  10. 写入文件的写入器对象: 写入文件的写入器对象,可选。默认为None
  11. 写入文件的参数: 写入文件的参数,可选。默认为None
    需要注意的是,headerfooter参数默认为None,表示不写入表头和表尾。如果希望写入表头或表尾,可以设置该参数为True
import pandas as pd
from itemadapter import ItemAdapterclass MyspiderPipeline:def process_item(self, item, spider):data = pd.DataFrame(dict(item))data.to_csv('data2.csv', mode='a+', index=None, encoding='utf-8-sig', header=None)return item