设为首页收藏本站

EPS数据狗论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3085|回复: 1

Scrapy框架在爬虫中的基本应用

  [复制链接]

14

主题

112

金钱

187

积分

入门用户

发表于 2019-9-9 15:18:09 | 显示全部楼层 |阅读模式

一、安装及常用命令介绍
1. 安装
Linux:pip3 install scrapy
Windows:
      a. pip3 install wheel
      b. 下载twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
      c. shift右击进入下载目录,执行 pip3 install typed_ast-1.4.0-cp36-cp36m-win32.whl
      d. pip3 install pywin32
      e. pip3 install scrapy

2.scrapy基本命令行
  1. (1)创建一个新的项目
  2. scrapy startproject ProjectName

  3. (2)生成爬虫
  4. scrapy genspider +SpiderName+website

  5. (3)运行(crawl)             # -o output
  6. scrapy crawl +SpiderName
  7. scrapy crawl SpiderName -o file.json
  8. scrapy crawl SpiderName-o file.csv

  9. (4)check检查错误
  10. scrapy check

  11. (5)list返回项目所有spider名称
  12. scrapy list

  13. (6)view 存储、打开网页
  14. scrapy view https://www.baidu.com

  15. (7)scrapy shell,进入终端
  16. scrapy shell https://www.baidu.com

  17. (8)scrapy runspider
  18. scrapy runspider zufang_spider.py
复制代码



二、简单实例
以麦田租房信息爬取为例,网站http://bj.maitian.cn/zfall/PG1
1.创建项目
  1. scrapy startproject houseinfo
复制代码


生成项目结构:
1.png
scrapy.cfg    项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中)
items.py      设置数据存储模板,用于结构化数据,如:Django的Model
pipelines     数据持久化处理
settings.py   配置文件
spiders       爬虫目录

2.创建爬虫应用程序
  1. cd houseinfo
  2. scrapy genspider maitian maitian.com
复制代码

然后就可以在spiders目录下看到我们的爬虫主程序
2.png

3.编写爬虫文件
  步骤2执行完毕后,会在项目的spiders中生成一个应用名的py爬虫文件,文件源码如下:
  1. # -*- coding: utf-8 -*-
  2. import scrapy


  3. class MaitianSpider(scrapy.Spider):
  4.     name = 'maitian'                        # 应用名称
  5.     allowed_domains = ['maitian.com']       #一般注释掉,允许爬取的域名(如果遇到非该域名的url则爬取不到数据)
  6.     start_urls = ['http://maitian.com/']    #起始爬取的url列表,该列表中存在的url,都会被parse进行请求的发送
  7.    
  8.     #解析函数
  9.     def parse(self, response):
  10.         pass
复制代码

我们可以在此基础上,根据需求进行编写
  1. # -*- coding: utf-8 -*-
  2. import scrapy

  3. class MaitianSpider(scrapy.Spider):
  4.     name = 'maitian'
  5.     start_urls = ['http://bj.maitian.cn/zfall/PG100']


  6.     #解析函数
  7.     def parse(self, response):

  8.         li_list = response.xpath('//div[@class="list_wrap"]/ul/li')
  9.         results = []
  10.         for li in li_list:
  11.             title =  li.xpath('./div[2]/h1/a/text()').extract_first().strip()
  12.             price = li.xpath('./div[2]/div/ol/strong/span/text()').extract_first().strip()
  13.             square = li.xpath('./div[2]/p[1]/span[1]/text()').extract_first().replace('㎡','')            # 将面积的单位去掉
  14.             area = li.xpath('./div[2]/p[2]/span/text()[2]').extract_first().strip().split('\xa0')[0]        # 以空格分隔
  15.             adress = li.xpath('./div[2]/p[2]/span/text()[2]').extract_first().strip().split('\xa0')[2]

  16.             dict = {
  17.                 "标题":title,
  18.                 "月租金":price,
  19.                 "面积":square,
  20.                 "区域":area,
  21.                 "地址":adress
  22.             }
  23.             results.append(dict)

  24.             print(title,price,square,area,adress)
  25.         return results
复制代码


须知:
xpath为scrapy中的解析方式
xpath函数返回的为列表,列表中存放的数据为Selector类型数据。解析到的内容被封装在Selector对象中,需要调用extract()函数将解析的内容从Selector中取出。
如果可以保证xpath返回的列表中只有一个列表元素,则可以使用extract_first(), 否则必须使用extract()

两者等同,都是将列表中的内容提取出来
title =  li.xpath('./div[2]/h1/a/text()').extract_first().strip()
title =  li.xpath('./div[2]/h1/a/text()')[0].extract().strip()


4. 设置修改settings.py配置文件相关配置:
  1. #伪装请求载体身份
  2. USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

  3. #可以忽略或者不遵守robots协议
  4. ROBOTSTXT_OBEY = False
复制代码


5.执行爬虫程序:scrapy crawl  maitain
3.png

爬取全站数据,也就是全部页码数据。本例中,总共100页,观察页面之间的共性,构造通用url
方式一:通过占位符,构造通用url
  1. import scrapy

  2. class MaitianSpider(scrapy.Spider):
  3.     name = 'maitian'
  4.     start_urls = ['http://bj.maitian.cn/zfall/PG{}'.format(page) for page in range(1,4)]            #注意写法


  5.     #解析函数
  6.     def parse(self, response):

  7.         li_list = response.xpath('//div[@class="list_wrap"]/ul/li')
  8.         results = []
  9.         for li in li_list:
  10.             title =  li.xpath('./div[2]/h1/a/text()').extract_first().strip()
  11.             price = li.xpath('./div[2]/div/ol/strong/span/text()').extract_first().strip()
  12.             square = li.xpath('./div[2]/p[1]/span[1]/text()').extract_first().replace('㎡','')
  13.             # 也可以通过正则匹配提取出来
  14.             area = li.xpath('./div[2]/p[2]/span/text()[2]')..re(r'昌平|朝阳|东城|大兴|丰台|海淀|石景山|顺义|通州|西城')[0]           
  15.             adress = li.xpath('./div[2]/p[2]/span/text()[2]').extract_first().strip().split('\xa0')[2]

  16.             dict = {
  17.                 "标题":title,
  18.                 "月租金":price,
  19.                 "面积":square,
  20.                 "区域":area,
  21.                 "地址":adress
  22.             }
  23.             results.append(dict)

  24.         return results
复制代码


如果碰到一个表达式不能包含所有情况的项目,解决方式是先分别写表达式,最后通过列表相加,将所有url合并成一个url列表,例如
  1. start_urls = ['http://www.guokr.com/ask/hottest/?page={}'.format(n) for n in range(1, 8)] + [
  2.      'http://www.guokr.com/ask/highlight/?page={}'.format(m) for m in range(1, 101)]
复制代码

方式二:通过重写start_requests方法,获取所有的起始url。(不用写start_urls)


三、数据持久化存储
  基于终端指令的持久化存储
  基于管道的持久化存储

只要是数据持久化存储,parse方法必须有返回值,也就是return后的内容

1. 基于终端指令的持久化存储
执行输出指定格式进行存储:将爬取到的数据写入不同格式的文件中进行存储,windows终端不能使用txt格式
    scrapy crawl 爬虫名称 -o xxx.json
    scrapy crawl 爬虫名称 -o xxx.xml
  scrapy crawl 爬虫名称 -o xxx.csv
以麦田为例,spider中的代码不变,将返回值写到qiubai.csv中。本地没有,就会自己创建一个。本地有就会追加
  1. scrapy crawl maitian   -o maitian.csv
复制代码

就会在项目目录下看到,生成的文件
4.png
查看文件内容
5.png

2.基于管道的持久化存储
scrapy框架中已经为我们专门集成好了高效、便捷的持久化操作功能,我们直接使用即可。要想使用scrapy的持久化操作功能,我们首先来认识如下两个文件:
items.py:数据结构模板文件。定义数据属性。
pipelines.py:管道文件。接收数据(items),进行持久化操作。

持久化流程:
① 爬虫文件爬取到数据解析后,需要将数据封装到items对象中。
② 使用yield关键字将items对象提交给pipelines管道,进行持久化操作。
③ 在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,然后编写持久化存储的代码,将item对象中存储的数据进行持久化存储(在管道的process_item方法中执行io操作,进行持久化存储)   
④ settings.py配置文件中开启管道

2.1保存到本地的持久化存储
爬虫文件:maitian.py
  1. import scrapy
  2. from houseinfo.items import HouseinfoItem               # 将item导入

  3. class MaitianSpider(scrapy.Spider):
  4.     name = 'maitian'
  5.     start_urls = ['http://bj.maitian.cn/zfall/PG100']

  6.     #解析函数
  7.     def parse(self, response):

  8.         li_list = response.xpath('//div[@class="list_wrap"]/ul/li')

  9.         for li in li_list:
  10.             item = HouseinfoItem(
  11.                 title =  li.xpath('./div[2]/h1/a/text()').extract_first().strip(),
  12.                 price = li.xpath('./div[2]/div/ol/strong/span/text()').extract_first().strip(),
  13.                 square = li.xpath('./div[2]/p[1]/span[1]/text()').extract_first().replace('㎡',''),
  14.                 area = li.xpath('./div[2]/p[2]/span/text()[2]').extract_first().strip().split('\xa0')[0],
  15.                 adress = li.xpath('./div[2]/p[2]/span/text()[2]').extract_first().strip().split('\xa0')[2]
  16.             )

  17.             yield item                      # 提交给管道,然后管道定义存储方式
复制代码


items文件:items.py
  1. import scrapy

  2. class HouseinfoItem(scrapy.Item):
  3.     title = scrapy.Field()          #存储标题,里面可以存储任意类型的数据
  4.     price = scrapy.Field()
  5.     square = scrapy.Field()
  6.     area = scrapy.Field()
  7.     adress = scrapy.Field()
复制代码


管道文件:pipelines.py
  1. class HouseinfoPipeline(object):
  2.     def __init__(self):
  3.         self.file = None

  4.     #开始爬虫时,执行一次
  5.     def open_spider(self,spider):
  6.         self.file = open('maitian.csv','a',encoding='utf-8')                    # 选用了追加模式
  7.         self.file.write(",".join(["标题","月租金","面积","区域","地址","\n"]))
  8.         print("开始爬虫")

  9.     # 因为该方法会被执行调用多次,所以文件的开启和关闭操作写在了另外两个只会各自执行一次的方法中。
  10.     def process_item(self, item, spider):
  11.         content = [item["title"], item["price"], item["square"], item["area"], item["adress"], "\n"]
  12.         self.file.write(",".join(content))
  13.         return item

  14.     # 结束爬虫时,执行一次
  15.     def close_spider(self,spider):
  16.         self.file.close()
  17.         print("结束爬虫")
复制代码


配置文件:settings.py
  1. #伪装请求载体身份
  2. USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

  3. #可以忽略或者不遵守robots协议
  4. ROBOTSTXT_OBEY = False  

  5. #开启管道
  6. ITEM_PIPELINES = {
  7.    'houseinfo.pipelines.HouseinfoPipeline': 300,                    #数值300表示为优先级,值越小优先级越高
  8. }
复制代码

6.png
7.png


四、爬取多级页面
爬取多级页面,会遇到2个问题:
问题1:如何对下一层级页面发送请求?
答:在每一个解析函数的末尾,通过Request方法对下一层级的页面手动发起请求
  1. # 先提取二级页面url,再对二级页面发送请求。多级页面以此类推
  2. def parse(self, response):
  3.     next_url = response.xpath('//div[2]/h2/a/@href').extract()[0]           # 提取二级页面url
  4.    yield scrapy.Request(url=next_url, callback=self.next_parse)               # 对二级页面发送请求,注意要用yield,回调函数不带括号
复制代码


问题2:解析的数据不在同一张页面中,最终如何将数据传递
答:涉及到请求传参,可以在对下一层级页面发送请求的时候,通过meta参数进行数据传递,meta字典就会传递给回调函数的response参数。下一级的解析函数通过response获取item. 先通过 response.meta返回接收到的meta字典,再获得item字典
  1. # 通过meta参数进行Request的数据传递,meta字典就会传递给回调函数的response参数
  2. def parse(self, response):
  3.     item = Item()                                                       # 实例化item对象
  4.     Item["field1"] = response.xpath('expression1').extract()[0]         # 列表中只有一个元素
  5.     Item["field2"] = response.xpath('expression2').extract()            # 列表
  6.     next_url = response.xpath('expression3').extract()[0]           # 提取二级页面url

  7.     # meta参数:请求传参.通过meta参数进行Request的数据传递,meta字典就会传递给回调函数的response参数
  8.     yield scrapy.Request(url=next_url, callback=self.next_parse,meta={'item':item})            # 对二级页面发送请求

  9. def next_parse(self,response):
  10.     # 通过response获取item. 先通过 response.meta返回接收到的meta字典,再获得item字典
  11.     item = response.meta['item']
  12.     item['field'] = response.xpath('expression').extract_first()
  13.     yield item                                                          #提交给管道
复制代码


案例1:麦田,对所有页码发送请求。不推荐将每一个页码对应的url存放到爬虫文件的起始url列表(start_urls)中。这里我们使用Request方法手动发起请求。
  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from houseinfo.items import HouseinfoItem               # 将item导入

  4. class MaitianSpider(scrapy.Spider):
  5.     name = 'maitian'
  6.     start_urls = ['http://bj.maitian.cn/zfall/PG{}'.format(page) for page in range(1,4)]

  7.     #爬取多页
  8.     page = 1
  9.     url = 'http://bj.maitian.cn/zfall/PG%d'

  10.     #解析函数
  11.     def parse(self, response):

  12.         li_list = response.xpath('//div[@class="list_wrap"]/ul/li')

  13.         for li in li_list:
  14.             item = HouseinfoItem(
  15.                 title =  li.xpath('./div[2]/h1/a/text()').extract_first().strip(),
  16.                 price = li.xpath('./div[2]/div/ol/strong/span/text()').extract_first().strip(),
  17.                 square = li.xpath('./div[2]/p[1]/span[1]/text()').extract_first().replace('㎡',''),
  18.                 area = li.xpath('./div[2]/p[2]/span/text()[2]').re(r'昌平|朝阳|东城|大兴|丰台|海淀|石景山|顺义|通州|西城')[0],           # 也可以通过正则匹配提取出来
  19.                 adress = li.xpath('./div[2]/p[2]/span/text()[2]').extract_first().strip().split('\xa0')[2]
  20.             )
  21.             ['http://bj.maitian.cn/zfall/PG{}'.format(page) for page in range(1, 4)]
  22.             yield item                                  # 提交给管道,然后管道定义存储方式

  23.         if self.page < 4:
  24.             self.page += 1
  25.             new_url = format(self.url%self.page)                             # 这里的%是拼接的意思
  26.             yield scrapy.Request(url=new_url,callback=self.parse)            # 手动发起一个请求,注意一定要写yield
复制代码


案例2:这个案例比较好的一点是,parse函数,既有对下一页的回调,又有对详情页的回调
  1. import scrapy

  2. class QuotesSpider(scrapy.Spider):
  3.     name = 'quotes_2_3'
  4.     start_urls = [
  5.         'http://quotes.toscrape.com',
  6.     ]
  7.     allowed_domains = [
  8.         'toscrape.com',
  9.     ]

  10.     def parse(self,response):
  11.         for quote in response.css('div.quote'):
  12.             yield{
  13.                 'quote': quote.css('span.text::text').extract_first(),
  14.                 'author': quote.css('small.author::text').extract_first(),
  15.                 'tags': quote.css('div.tags a.tag::text').extract(),
  16.             }
  17.             author_page = response.css('small.author+a::attr(href)').extract_first()
  18.             authro_full_url = response.urljoin(author_page)
  19.             yield scrapy.Request(authro_full_url, callback=self.parse_author)        # 对详情页发送请求,回调详情页的解析函数
  20.             
  21.         next_page = response.css('li.next a::attr("href")').extract_first()      # 通过css选择器定位到下一页
  22.         if next_page is not None:
  23.             next_full_url = response.urljoin(next_page)
  24.             yield scrapy.Request(next_full_url, callback=self.parse)               # 对下一页发送请求,回调自己的解析函数

  25.     def parse_author(self,response):
  26.         yield{
  27.             'author': response.css('.author-title::text').extract_first(),
  28.             'author_born_date': response.css('.author-born-date::text').extract_first(),
  29.             'author_born_location': response.css('.author-born-location::text').extract_first(),
  30.             'authro_description': response.css('.author-born-location::text').extract_first(),
复制代码


案例3:爬取www.id97.com电影网,将一级页面中的电影名称,类型,评分,二级页面中的上映时间,导演,片长进行爬取。(多级页面+传参)
  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from moviePro.items import MovieproItem
  4. class MovieSpider(scrapy.Spider):
  5.     name = 'movie'
  6.     allowed_domains = ['www.id97.com']
  7.     start_urls = ['http://www.id97.com/']

  8.     def parse(self, response):
  9.         div_list = response.xpath('//div[@class="col-xs-1-5 movie-item"]')

  10.         for div in div_list:
  11.             item = MovieproItem()                    item['name'] = div.xpath('.//h1/a/text()').extract_first()
  12.             item['score'] = div.xpath('.//h1/em/text()').extract_first()

  13.             item['kind'] = div.xpath('.//div[@class="otherinfo"]').xpath('string(.)').extract_first()
  14.             item['detail_url'] = div.xpath('./div/a/@href').extract_first()

  15.             #meta参数:请求传参.通过meta参数进行Request的数据传递,meta字典就会传递给回调函数的response参数
  16.             yield scrapy.Request(url=item['detail_url'],callback=self.parse_detail,meta={'item':item})   

  17. def parse_detail(self,response):
  18.         #通过response获取item. 先通过 response.meta返回接收到的meta字典,再获得item字典
  19.         item = response.meta['item']
  20.         item['actor'] = response.xpath('//div[@class="row"]//table/tr[1]/a/text()').extract_first()
  21.         item['time'] = response.xpath('//div[@class="row"]//table/tr[7]/td[2]/text()').extract_first()
  22.         item['long'] = response.xpath('//div[@class="row"]//table/tr[8]/td[2]/text()').extract_first()
  23.         yield item                                                    #提交item到管道
复制代码


案例4:稍复杂,可参考链接进行理解:
https://github.com/makcyun/web_scraping_with_python/tree/master/,
https://www.cnblogs.com/sanduzxcvbnm/p/10277414.html
  1. #!/user/bin/env python

  2. """
  3. 爬取豌豆荚网站所有分类下的全部 app
  4. 数据爬取包括两个部分:
  5. 一:数据指标
  6. 1 爬取首页
  7. 2 爬取第2页开始的 ajax 页
  8. 二:图标
  9. 使用class方法下载首页和 ajax 页
  10. 分页循环两种爬取思路,
  11. 指定页数进行for 循环,和不指定页数一直往下爬直到爬不到内容为止
  12. 1 for 循环
  13. """

  14. import scrapy
  15. from wandoujia.items import WandoujiaItem

  16. import requests
  17. from pyquery import PyQuery as pq
  18. import re
  19. import csv
  20. import pandas as pd
  21. import numpy as np
  22. import time
  23. import pymongo
  24. import json
  25. import os
  26. from urllib.parse import urlencode
  27. import random
  28. import logging

  29. logging.basicConfig(filename='wandoujia.log',filemode='w',level=logging.DEBUG,format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S %p')
  30. # https://juejin.im/post/5aee70105188256712786b7f
  31. logging.warning("warn message")
  32. logging.error("error message")


  33. class WandouSpider(scrapy.Spider):
  34.     name = 'wandou'
  35.     allowed_domains = ['www.wandoujia.com']
  36.     start_urls = ['http://www.wandoujia.com/']

  37.     def __init__(self):
  38.         self.cate_url = 'https://www.wandoujia.com/category/app'
  39.         # 首页url
  40.         self.url = 'https://www.wandoujia.com/category/'
  41.         # ajax 请求url
  42.         self.ajax_url = 'https://www.wandoujia.com/wdjweb/api/category/more?'
  43.         # 实例化分类标签
  44.         self.wandou_category = Get_category()

  45.     def start_requests(self):
  46.         yield scrapy.Request(self.cate_url,callback=self.get_category)
  47.         
  48.     def get_category(self,response):   
  49.         # # num = 0
  50.         cate_content = self.wandou_category.parse_category(response)
  51.         for item in cate_content:
  52.             child_cate = item['child_cate_codes']
  53.             for cate in child_cate:
  54.                 cate_code = item['cate_code']
  55.                 cate_name = item['cate_name']
  56.                 child_cate_code = cate['child_cate_code']
  57.                 child_cate_name = cate['child_cate_name']

  58.       
  59.         # # 单类别下载
  60.         # cate_code = 5029
  61.         # child_cate_code = 837
  62.         # cate_name = '通讯社交'
  63.         # child_cate_name = '收音机'
  64.         
  65.                 # while循环
  66.                 page = 1 # 设置爬取起始页数
  67.                 print('*' * 50)

  68.                 # # for 循环下一页
  69.                 # pages = []
  70.                 # for page in range(1,3):
  71.                 # print('正在爬取:%s-%s 第 %s 页 ' %
  72.                 # (cate_name, child_cate_name, page))
  73.                 logging.debug('正在爬取:%s-%s 第 %s 页 ' %
  74.                 (cate_name, child_cate_name, page))

  75.                 if page == 1:
  76.                     # 构造首页url
  77.                     category_url = '{}{}_{}' .format(self.url, cate_code, child_cate_code)
  78.                 else:
  79.                     params = {
  80.                     'catId': cate_code,  # 大类别
  81.                     'subCatId': child_cate_code,  # 小类别
  82.                     'page': page,
  83.                     }
  84.                     category_url = self.ajax_url + urlencode(params)

  85.                 dict = {'page':page,'cate_name':cate_name,'cate_code':cate_code,'child_cate_name':child_cate_name,'child_cate_code':child_cate_code}
  86.                     
  87.                 yield scrapy.Request(category_url,callback=self.parse,meta=dict)
  88.                            
  89.                 #     # for 循环方法
  90.                 #     pa = yield scrapy.Request(category_url,callback=self.parse,meta=dict)
  91.                 #     pages.append(pa)
  92.                 # return pages

  93.     def parse(self, response):
  94.         if len(response.body) >= 100:  # 判断该页是否爬完,数值定为100是因为无内容时长度是87
  95.             page = response.meta['page']
  96.             cate_name = response.meta['cate_name']
  97.             cate_code = response.meta['cate_code']
  98.             child_cate_name = response.meta['child_cate_name']
  99.             child_cate_code = response.meta['child_cate_code']

  100.             if page == 1:
  101.                 contents = response
  102.             else:
  103.                 jsonresponse = json.loads(response.body_as_unicode())
  104.                 contents = jsonresponse['data']['content']
  105.                 # response 是json,json内容是html,html 为文本不能直接使用.css 提取,要先转换
  106.                 contents = scrapy.Selector(text=contents, type="html")

  107.             contents = contents.css('.card')
  108.             for content in contents:
  109.                 # num += 1
  110.                 item = WandoujiaItem()
  111.                 item['cate_name'] = cate_name
  112.                 item['child_cate_name'] = child_cate_name
  113.                 item['app_name'] = self.clean_name(content.css('.name::text').extract_first())
  114.                 item['install'] = content.css('.install-count::text').extract_first()
  115.                 item['volume'] = content.css('.meta span:last-child::text').extract_first()
  116.                 item['comment'] = content.css('.comment::text').extract_first().strip()
  117.                 item['icon_url'] = self.get_icon_url(content.css('.icon-wrap a img'),page)
  118.                 yield item
  119.             
  120.             # 递归爬下一页
  121.             page += 1
  122.             params = {
  123.                     'catId': cate_code,  # 大类别
  124.                     'subCatId': child_cate_code,  # 小类别
  125.                     'page': page,
  126.                     }
  127.             ajax_url = self.ajax_url + urlencode(params)
  128.             
  129.             dict = {'page':page,'cate_name':cate_name,'cate_code':cate_code,'child_cate_name':child_cate_name,'child_cate_code':child_cate_code}
  130.             yield scrapy.Request(ajax_url,callback=self.parse,meta=dict)
  131.                


  132.         # 名称清除方法1 去除不能用于文件命名的特殊字符
  133.     def clean_name(self, name):
  134.         rule = re.compile(r"[\/\\\:\*\?"\<\>\|]")  # '/ \ : * ? " < > |')
  135.         name = re.sub(rule, '', name)
  136.         return name

  137.     def get_icon_url(self,item,page):
  138.         if page == 1:
  139.             if item.css('::attr("src")').extract_first().startswith('https'):
  140.                 url = item.css('::attr("src")').extract_first()
  141.             else:
  142.                 url = item.css('::attr("data-original")').extract_first()
  143.         # ajax页url提取
  144.         else:
  145.             url = item.css('::attr("data-original")').extract_first()

  146.         # if url:  # 不要在这里添加url存在判断,否则空url 被过滤掉 导致编号对不上
  147.         return url


  148. # 首先获取主分类和子分类的数值代码 # # # # # # # # # # # # # # # #
  149. class Get_category():
  150.     def parse_category(self, response):
  151.         category = response.css('.parent-cate')
  152.         data = [{
  153.             'cate_name': item.css('.cate-link::text').extract_first(),
  154.             'cate_code': self.get_category_code(item),
  155.             'child_cate_codes': self.get_child_category(item),
  156.         } for item in category]
  157.         return data

  158.     # 获取所有主分类标签数值代码
  159.     def get_category_code(self, item):
  160.         cate_url = item.css('.cate-link::attr("href")').extract_first()

  161.         pattern = re.compile(r'.*/(\d+)')  # 提取主类标签代码
  162.         cate_code = re.search(pattern, cate_url)
  163.         return cate_code.group(1)

  164.     # 获取所有子分类标签数值代码
  165.     def get_child_category(self, item):
  166.         child_cate = item.css('.child-cate a')
  167.         child_cate_url = [{
  168.             'child_cate_name': child.css('::text').extract_first(),
  169.             'child_cate_code': self.get_child_category_code(child)
  170.         } for child in child_cate]

  171.         return child_cate_url

  172.     # 正则提取子分类
  173.     def get_child_category_code(self, child):
  174.         child_cate_url = child.css('::attr("href")').extract_first()
  175.         pattern = re.compile(r'.*_(\d+)')  # 提取小类标签编号
  176.         child_cate_code = re.search(pattern, child_cate_url)
  177.         return child_cate_code.group(1)

  178.     # # 可以选择保存到txt 文件
  179.     # def write_category(self,category):
  180.     #     with open('category.txt','a',encoding='utf_8_sig',newline='') as f:
  181.     #         w = csv.writer(f)
  182.     #         w.writerow(category.values())
复制代码

以上4个案例都只贴出了爬虫主程序脚本,因篇幅原因,所以item、pipeline和settings等脚本未贴出,可参考上面案例进行编写。


五、Scrapy发送post请求
问题:在之前代码中,我们从来没有手动的对start_urls列表中存储的起始url进行过请求的发送,但是起始url的确是进行了请求的发送,那这是如何实现的呢?
解答:其实是因为爬虫文件中的爬虫类继承到了Spider父类中的start_requests(self)这个方法,该方法就可以对start_urls列表中的url发起请求:
  1. def start_requests(self):
  2.         for u in self.start_urls:
  3.            yield scrapy.Request(url=u,callback=self.parse)
复制代码

注意:该方法默认的实现,是对起始的url发起get请求,如果想发起post请求,则需要子类重写该方法。不过,一般情况下不用scrapy发post请求,用request模块。

例:爬取百度翻译
  1. # -*- coding: utf-8 -*-
  2. import scrapy

  3. class PostSpider(scrapy.Spider):
  4.     name = 'post'
  5.     # allowed_domains = ['www.xxx.com']
  6.     start_urls = ['https://fanyi.baidu.com/sug']

  7.     def start_requests(self):
  8.         data = {                                                # post请求参数
  9.             'kw':'dog'
  10.         }
  11.         for url in self.start_urls:
  12.             yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse)        # 发送post请求

  13.     def parse(self, response):
  14.         print(response.text)
复制代码


六、设置日志等级
- 在使用scrapy crawl spiderFileName运行程序时,在终端里打印输出的就是scrapy的日志信息。
  - 日志信息的种类:
        ERROR : 一般错误
        WARNING : 警告
        INFO : 一般的信息
        DEBUG : 调试信息   

  - 设置日志信息指定输出:
    在settings.py配置文件中,加入
                    LOG_LEVEL = ‘指定日志信息种类’即可。
                    LOG_FILE = 'log.txt'则表示将日志信息写入到指定文件中进行存储。

0

主题

26

金钱

37

积分

新手用户

发表于 2019-12-3 14:53:46 | 显示全部楼层
先收藏一下吧,不错
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

客服中心
关闭
在线时间:
周一~周五
8:30-17:30
QQ群:
653541906
联系电话:
010-85786021-8017
在线咨询
客服中心

意见反馈|网站地图|手机版|小黑屋|EPS数据狗论坛 ( 京ICP备09019565号-3 )   

Powered by BFIT! X3.4

© 2008-2028 BFIT Inc.

快速回复 返回顶部 返回列表