Scrapy

date
Apr 22, 2022
slug
scrapy
status
Published
tags
Scrapy
summary
Scrapy说明
type
Post

框架的选择

Scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。。用这个框架可以轻松爬下来如亚马逊商品信息之类的数据。

PySpider

pyspider 是一个用python实现的功能强大的网络爬虫系统,能在浏览器界面上进行脚本的编写,功能的调度和爬取结果的实时查看,后端使用常用的数据库进行爬取结果的存储,还能定时设置任务与任务优先级等。

Crawley

Crawley可以高速爬取对应网站的内容,支持关系和非关系数据库,数据可以导出为JSON、XML等。

Portia

Portia是一个开源可视化爬虫工具,可让您在不需要任何编程知识的情况下爬取网站!简单地注释您感兴趣的页面,Portia将创建一个蜘蛛来从类似的页面提取数据。

Scrapy基本教程

 
xpath如何抓取
notion image
创建项目
scrapy startproject coolscrapy
运行爬虫
scrapy crawl huxiu

参数传递

1、爬取列表信息的方法funcA
2、爬取新闻详情信息的funcB
3、爬取解说的funcC
建议用深拷贝
from copy import deepcopy

def parse(self, response):
     # collect `item_urls` 
     for item_url in item_urls:
         yield Request(url=item_url, callback=self.funcA)

 def funcA(self, response):
     item = MyItem()
     # 处理列表数据
     yield Request(url=item_details_url, meta={'item': deepcopy(item)},
            callback=self.funcB)

 def funcB(self, response):
     item = response.meta['item']
     # 处理新闻详情页的数据
     yield Request(url=item_details_url, meta={'item': deepcopy(item)},
            callback=self.funcC)

  def funcC(self, response):
     item = response.meta['item']
     # 处理新闻解说页的数据
     return item

scrapy中的yield scrapy.Request 在传递item 的注意点

深拷贝
在用scrapy框架的时候在很多情况下会出现要爬取一个列表页面和一个详情页面的情况,这个时候通常会使用yield 来发起一个请求,并通过 callback 参数为这个请求添加回调函数,在请求完成之后会将响应作为参数传递给回调函数,但在我们传递item的时候会出现一些问题:
notion image
在需要多次调用下面这个parse_detail() 方法的时候,会出现获取到最后一个item的情况,而且是循环调用最后一个,就像是上面yield 这一部分是个for循环,但是下面的parse方法不再循环内,所以就只能一直调用到最后一个item.
解决方法:
notion image
使用deepcopy深层次拷贝item,拷贝每一次引擎收到的item,并将其调给parse_detail()方法

日志配置

提供5层logging级别:
1. CRITICAL - 严重错误
2. ERROR - 一般错误
3. WARNING - 警告信息
4. INFO - 一般信息
5. DEBUG - 调试信息
通过在setting.py中进行以下设置可以被用来配置logging
默认值:
# 是否启用日志
LOG_ENABLED=True

# 日志使用的编码
LOG_ENCODING='utf-8'

# 日志文件(文件名)
LOG_FILE=None

# 日志格式
LOG_FORMAT='%(asctime)s [%(name)s] %(levelname)s: %(message)s'

# 日志时间格式
LOG_DATEFORMAT='%Y-%m-%d %H:%M:%S'

# 日志级别 CRITICAL, ERROR, WARNING, INFO, DEBUG
LOG_LEVEL='DEBUG'

# 如果等于True,所有的标准输出(包括错误)都会重定向到日志,例如:print('hello')
LOG_STDOUT=False

# 如果等于True,日志仅仅包含根路径,False显示日志输出组件
LOG_SHORT_NAMES=False
配置示例
# setting.py

from datetime import datetime

# 文件及路径,log目录需要先建好
today = datetime.now()
log_file_path = "log/scrapy_{}_{}_{}.log".format(today.year, today.month, today.day)

# 日志输出
LOG_LEVEL = 'DEBUG'
LOG_FILE = log_file_path
使用
import logging
logger = logging.getLogger(__name__)
logger.warning("This is a warning")
或者
import scrapy

class MySpider(scrapy.Spider):

    name = 'myspider'
    start_urls = ['https://scrapinghub.com']

    def parse(self, response):
        self.logger.info('Parse function called on %s', response.url)

使用settings配置

可以在自定义 .py 文件,也是比较推荐的
mysettings.py
reids_conf = {
  'host': '127.0.0.1',
  'port': 6379,
  'password': '123456',
  'database': 0
}
mytest.py中使用
from mysettings import redis_conf

print(redis_conf['host'])

301的情况

在settings中设置
# 这其实是网站的一种反爬机制,我们要获取的资源被重定向到新的url
MEDIA_ALLOW_REDIRECTS = True

403的情况

在爬 https://hotel.meituan.com/hangzhou/ 美团的一个酒店信息时候,浏览器链接访问正常,但是爬虫报错如下:
...
019-10-23 11:45:54 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-10-23 11:45:54 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2019-10-23 11:45:54 [scrapy.core.engine] DEBUG: Crawled (403) <GET http://hotel.meituan.com/robots.txt> (referer: None)
2019-10-23 11:45:54 [scrapy.core.engine] DEBUG: Crawled (403) <GET http://hotel.meituan.com/hangzhou/> (referer: None)
2019-10-23 11:45:54 [scrapy.spidermiddlewares.httperror] INFO: Ignoring response <403 http://hotel.meituan.com/hangzhou/>: HTTP status code is not handled or not allowed
2019-10-23 11:45:54 [scrapy.core.engine] INFO: Closing spider (finished)

主要提示在这一行
2019-10-23 11:45:54 [scrapy.spidermiddlewares.httperror] INFO: Ignoring response <403 http://hotel.meituan.com/hangzhou/>: HTTP status code is not handled or not allowed
1
但是访问网站十分正常,后来查看了美团的robots.txt也没有做任何的禁止
解决思路 在默认情况下,scrapy 是不会模拟浏览器去获取信息的,而 HTTP 403 表示服务器获得了请求,但是拒绝提供服务。那么就需要我们配置 用户代理(User-Agent)到scrapy中,对发起的请求进行模拟:
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
Crawl responsibly by identifying yourself (and your website) on the user-agent
被修改的文件为 setting.py 文件,在 scrapy 框架中路径如下:
添加了 user-agent 后,就可以正常访问网站了。

ImagePipeline

首先要继承ImagePipeline
class MyImagePipeline(ImagesPipeline):
	# 返回图片的存放路径
    # 该方法用来对图片url发起请求
    def get_media_requests(self, item, info):
        # print('开始下载')
        wenjianjia = item['title']  # 分类文件夹,
        image_download_prefix_path = item['image_download_prefix_path']  # 图片下载路径前缀
        # headers = {'referer': item['image_referer']} # 可以添加referer
        # 在去下载image_urls, 先判断有没有存在,避免重复下载
        for url in item['image_urls']:
            file_name = url.split('/')[-1]
            image_path = os.path.join(image_download_prefix_path + wenjianjia, file_name)
            if not os.path.exists(IMAGES_STORE + image_path):
                yield scrapy.Request(url, meta={'item': item})
            else:
                logger.warning(f"文件已存在 {IMAGES_STORE + image_path}")
                pass

    # 该方法是用来设置图片的下载路径以及图片的名字
    def file_path(self, request, response=None, info=None):
        '''
        根目录,也就是settings文件下创建的存储图片根目录
        注意:根目录的设置的时候,不要加“./,否则下面创建文件夹的时候,会自动创建一个根目录名字的文件夹
        '''
        item = request.meta['item']  # 实体
        wenjianjia = item['title']  # 分类文件夹,
        image_download_prefix_path = item['image_download_prefix_path']  # 图片下载路径前缀
        # 在settings下生成的路径
        url = request.url
        file_name = url.split('/')[-1]

        # 图片存放路径
        image_path = os.path.join(image_download_prefix_path + wenjianjia, file_name)
        logger.warning(f"下载文件: 保存地址= {IMAGES_STORE + image_path} , 图片地址={url}")
        # 返回图片的存放路径
        return image_path

    def item_completed(self, results, item, info):
        # print('下载完成')
        return item

yield的说明

Scrapyd部署

 

Chrome的Network抓包小技巧

配合postman测试接口

首先找到chrome的network,找到请求详情,copy curl的方式
notion image
打开postman, 把刚才复制的请求贴进去,然后选择continue
notion image

Selemium使用

 

© WangJiaHao 2022