Scrapy 框架学习

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Scrapy 框架学习

案例 jd图书爬虫

jd图书网站爬取比较容易,主要是数据的提取

spider 代码:

import scrapy
from jdbook.pipelines import JdbookPipeline
import re
from copy import deepcopy


class JdbookspiderSpider(scrapy.Spider):
    name = 'jdbookspider'
    allowed_domains = ['jd.com']
    start_urls = ['https://book.jd.com/booksort.html']
    # 处理分类页面的数据
    def parse(self, response):
        # 这里借助了selenium 先访问jd图书网,因为直接get请求jdbook 获取到只是一堆js代码,没有有用的html元素,通过selenium正常访问网页,将page_source(就是当前网页的页面内容,selenium提供的属性)返回给spider进行数据处理
        # 处理大分类的列表页
        response_data, driver = JdbookPipeline.gain_response_data(url='https://book.jd.com/booksort.html')
        driver.close()
        item = {
   
   }
        # 由于selenium返回的page_source是字符串,所以不能直接使用xpath,使用了正则(也可以借助bs4 再使用正则)
        middle_group_link = re.findall('<em>.*?<a href="(.*?)">.*?</a>.*?</em>', response_data, re.S)
        big_group_name = re.findall('<dt>.*?<a href=".*?">(.*?)</a>.*?<b>.*?</b>.*?</dt>', response_data, re.S)
        big_group_link = re.findall('<dt>.*?<a href=".*?channel.jd.com/(.*?)\.html">.*?</a>.*?<b>.*?</b>.*?</dt>', response_data, re.S)
        middle_group_name = re.findall('<em>.*?<a href=".*?">(.*?)</a>.*?</em>', response_data, re.S)
        for i in range(len(middle_group_link)):
            var = str(middle_group_link[i])
            var1 = var[:var.find("com") + 4]
            var2 = var[var.find("com") + 4:]
            var3 = var2.replace("-", ",").replace(".html", "")
            var_end = "https:" + var1 + "list.html?cat=" + var3
            for j in range(len(big_group_name)):
                temp_ = var_end.find(str(big_group_link[j]).replace("-", ","))
                if temp_ != -1:
                    item["big_group_name"] = big_group_name[j]
                    item["big_group_link"] = big_group_link[j]
            item["middle_group_link"] = var_end
            item["middle_group_name"] = middle_group_name[i]
            # 请求大分组下的小分组的详情页
            if var_end is not None:
                yield scrapy.Request(
                    var_end,
                    callback=self.parse_detail,
                    meta={
   
   "item": deepcopy(item)}
                )
    # 处理图书列表页的数据
    def parse_detail(self, response):
        print(response.url)
        item = response.meta["item"]
        detail_name_list = re.findall('<div class="gl-i-wrap">.*?<div class="p-name">.*?<a target="_blank" title=".*?".*?<em>(.*?)</em>', response.body.decode(), re.S)
        detail_content_list = re.findall(
            '<div class="gl-i-wrap">.*?<div class="p-name">.*?<a target="_blank" title="(.*?)"', response.body.decode(),
            re.S)
        detail_link_list = re.findall('<div class="gl-i-wrap">.*?<div class="p-name">.*?<a target="_blank" title=".*?" href="(.*?)"', response.body.decode(), re.S)
        detail_price_list = re.findall('<div class="p-price">.*?<strong class="J_.*?".*?data-done="1".*?>.*?<em>¥</em>.*?<i>(.*?)</i>', response.body.decode(), re.S)
        page_number_end = re.findall('<span class="fp-text">.*?<b>.*?</b>.*?<em>.*?</em>.*?<i>(.*?)</i>.*?</span>', response.body.decode(), re.S)[0]
        print(len(detail_price_list))
        print(len(detail_name_list))
        for i in range(len(detail_name_list)):
            detail_link = detail_link_list[i]
            item["detail_name"] = detail_name_list[i]
            item["detail_content"] = detail_content_list[i]
            item["detail_link"] = "https:" + detail_link
            item["detail_price"] = detail_price_list[i]
            yield item
        # 翻页
        for i in range(int(page_number_end)):

            next_url = item["middle_group_link"] + "&page=" + str(2*(i+1) + 1) + "&s=" + str(60*(i+1)) + "&click=0"
            yield scrapy.Request(
                next_url,
                callback=self.parse_detail,
                meta={
   
   "item": deepcopy(item)}
            )

pipeline 代码:

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
import csv

from itemadapter import ItemAdapter
from selenium import webdriver
import time


class JdbookPipeline:
     # 将数据写入csv文件
    def process_item(self, item, spider):
        with open('./jdbook.csv', 'a+', encoding='utf-8') as file:
            fieldnames = ['big_group_name', 'big_group_link', 'middle_group_name', 'middle_group_link', 'detail_name',
                          'detail_content', 'detail_link', 'detail_price']
            writer = csv.DictWriter(file, fieldnames=fieldnames)
            writer.writerow(item)
        return item

    def open_spider(self, spider):
        with open('./jdbook.csv', 'w+', encoding='utf-8') as file:
            fieldnames = ['big_group_name', 'big_group_link', 'middle_group_name', 'middle_group_link', 'detail_name',
                          'detail_content', 'detail_link', 'detail_price']
            writer = csv.DictWriter(file, fieldnames=fieldnames)
            writer.writeheader()
    # 提供的正常访问jdbook 方法,借助selenium
    @staticmethod
    def gain_response_data(url):
        drivers = webdriver.Chrome("E:\python_study\spider\data\chromedriver_win32\chromedriver.exe")
        drivers.implicitly_wait(2)
        drivers.get(url)
        drivers.implicitly_wait(2)
        time.sleep(2)
        # print(tb_cookie)
        return drivers.page_source, drivers

案例 当当图书爬虫

当当网的爬取也是比较容易, 但是这里需要结合scrapy-redis来实现分布式爬取数据

import urllib
from copy import deepcopy
import scrapy
from scrapy_redis.spiders import RedisSpider
import re


# 不再是继承Spider类,而是继承自scrapy_redis的RedisSpider类
class DangdangspiderSpider(RedisSpider):
    name = 'dangdangspider'
    allowed_domains = ['dangdang.com']
    # http://book.dangdang.com/
    # 同时,start_urls 也不在使用, 而是定义一个redis_key, spider要爬取的request对象就以该值为key, url为值存储在redis中,spider爬取时就从redis 中获取
    redis_key = "dangdang"
    # 处理图书分类数据
    def parse(self, response):
        div_list = response.xpath("//div[@class='con flq_body']/div")
        for div in div_list:
            item = {
   
   }
            item["b_cate"] = div.xpath("./dl/dt//text()").extract()
            item["b_cate"] = [i.strip() for i in item["b_cate"] if len(i.strip()) > 0]
            # 中间分类分组
            if len(item["b_cate"]) > 0:
                div_data = str(div.extract())
                dl_list = re.findall('''<dl class="inner_dl" ddt-area="\d+" dd_name=".*?">.*?<dt>(.*?)</dt>''',
                                     div_data, re.S)
                for dl in dl_list:
                    if len(str(dl)) > 100:
                        dl = re.findall('''.*?title="(.*?)".*?''', dl, re.S)
                    item["m_cate"] = str(dl).replace(" ", "").replace("\r\n", "")
                    # 小分类分组
                    a_link_list = re.findall(
                        '''<a class=".*?" href="(.*?)" target="_blank" title=".*?" nname=".*?" ddt-src=".*?">.*?</a>''',
                        div_data, re.S)
                    a_cate_list = re.findall(
                        '''<a class=".*?" href=".*?" target="_blank" title=".*?" nname=".*?" ddt-src=".*?">(.*?)</a>''',
                        div_data, re.S)
                    print(a_cate_list)
                    print(a_link_list)
                    for a in range(len(a_link_list)):
                        item["s_href"] = a_link_list[a]
                        item["s_cate"] = a_cate_list[a]

                        if item["s_href"] is not None:
                            yield scrapy.Request(
                                item["s_href"],
                                callback=self.parse_book_list,
                                meta={
   
   "item": deepcopy(item)}
                            )
    # 处理图书列表页数据
    def parse_book_list(self, response):
        item = response.meta["item"]
        li_list = response.xpath("//ul[@class='bigimg']/li")
        # todo 改进,对不同的图书列表页做不同的处理
        # if li_list is None:
        #     print(True)

        for li in li_list:
            item["book_img"] = li.xpath('./a[1]/img/@src').extract_first()
            if item["book_img"] is None:
                item["book_img"] = li.xpath("//ul[@class='list_aa ']/li").extract_first()
            item["book_name"] = li.xpath("./p[@class='name']/a/@title").extract_first()
            item["book_desc"] = li.xpath("./p[@class='detail']/text()").extract_first()
            item["book_price"] = li.xpath(".//span[@class='search_now_price']/text()").extract_first()
            item["book_author"] = li.xpath("./p[@class='search_book_author']/span[1]/a/text()").extract_first()
            item["book_publish_date"] = li.xpath("./p[@class='search_book_author']/span[2]/text()").extract_first()
            item["book_press"] = li.xpath("./p[@class='search_book_author']/span[3]/a/text()").extract_first()
            next_url = response.xpath("//li[@class='next']/a/@href").extract_first()
            yield item
            if next_url is not None:
                next_url = urllib.parse.urljoin(response.url, next_url)
                yield scrapy.Request(
                    next_url,
                    callback=self.parse_book_list,
                    meta={
   
   "item": item}
                )

pipeline 代码:

import csv

from itemadapter import ItemAdapter


class DangdangbookPipeline:
    # 将数据写入到csv文件中
    def process_item(self, item, spider):
        with open('./dangdangbook.csv', 'a+', encoding='utf-8', newline='') as file:
            fieldnames = ['b_cate', 'm_cate', 's_cate', 's_href', 'book_img', 'book_name', 'book_desc', 'book_price', 'book_author', 'book_publish_date', 'book_press']
            writer = csv.DictWriter(file, fieldnames=fieldnames)
            writer.writerow(item)
        return item

    def open_spider(self, spider):
        with open('./dangdangbook.csv', 'w+', encoding='utf-8', newline='') as file:
            fieldnames = ['b_cate', 'm_cate', 's_cate', 's_href', 'book_img', 'book_name', 'book_desc', 'book_price',
                          'book_author', 'book_publish_date', 'book_press']
            writer = csv.DictWriter(file, fieldnames=fieldnames)
            writer.writeheader()

settings 代码:

BOT_NAME = 'dangdangbook'

SPIDER_MODULES = ['dangdangbook.spiders']
NEWSPIDER_MODULE = 'dangdangbook.spiders'
# 需要scrapy-redis 的去重功能,这里引用
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 以及调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
# LOG_LEVEL = 'WARNING'
# 设置redis 的服务地址
REDIS_URL = 'redis://127.0.0.1:6379'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

ITEM_PIPELINES = {
   
   
   'dangdangbook.pipelines.DangdangbookPipeline': 300,
}

crontab 定时执行

image.png

image.png

以上都在Linux平台的直接操作crontab。

在python环境下我们可以借助pycrontab 来操作crontab 来设置定时任务。

补充

自定义的excel 到导出文件格式代码:

from scrapy.exporters import BaseItemExporter
import xlwt
class ExcelItemExporter(BaseItemExporter):
    def __init__(self, file, **kwargs):
        self._configure(kwargs)
        self.file = file
        self.wbook = xlwt.Workbook()
        self.wsheet = self.wbook.add_sheet('scrapy')
        self.row = 0
    def finish_exporting(self):
        self.wbook.save(self.file)
    def export_item(self, item):
        fields = self._get_serialized_fields(item)
        for col, v in enumerate(x for _, x in fields):
            self.wsheet.write(self.row, col, v)
        self.row += 1
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
113 6
|
2月前
|
数据采集 中间件 开发者
Scrapy爬虫框架-自定义中间件
Scrapy爬虫框架-自定义中间件
64 1
|
2月前
|
数据采集 中间件 Python
Scrapy爬虫框架-通过Cookies模拟自动登录
Scrapy爬虫框架-通过Cookies模拟自动登录
122 0
|
5月前
|
数据采集 存储 中间件
高效数据抓取:Scrapy框架详解
高效数据抓取:Scrapy框架详解
|
1月前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
89 4
|
2月前
|
数据采集 中间件 数据挖掘
Scrapy 爬虫框架(一)
Scrapy 爬虫框架(一)
59 0
|
2月前
|
数据采集 XML 前端开发
Scrapy 爬虫框架(二)
Scrapy 爬虫框架(二)
52 0
|
4月前
|
数据采集 存储 XML
Scrapy框架实现数据采集的详细步骤
本文介绍了使用Scrapy框架从宁波大学经济学院网站爬取新闻或公告详情页内容的过程,包括创建Scrapy项目、编写爬虫规则、提取所需信息,并最终将数据存储到Excel文件中的方法和步骤。
Scrapy框架实现数据采集的详细步骤
|
4月前
|
数据采集 中间件 调度
Scrapy 爬虫框架的基本使用
Scrapy 爬虫框架的基本使用
|
4月前
|
数据采集 存储 中间件
Python进行网络爬虫:Scrapy框架的实践
【8月更文挑战第17天】网络爬虫是自动化程序,用于从互联网收集信息。Python凭借其丰富的库和框架成为构建爬虫的首选语言。Scrapy作为一款流行的开源框架,简化了爬虫开发过程。本文介绍如何使用Python和Scrapy构建简单爬虫:首先安装Scrapy,接着创建新项目并定义爬虫,指定起始URL和解析逻辑。运行爬虫可将数据保存为JSON文件或存储到数据库。此外,Scrapy支持高级功能如中间件定制、分布式爬取、动态页面渲染等。在实践中需遵循最佳规范,如尊重robots.txt协议、合理设置爬取速度等。通过本文,读者将掌握Scrapy基础并了解如何高效地进行网络数据采集。
254 6

相关实验场景

更多