Scrapy 是 Python 开发的一个快速,高层次的屏幕抓取和 web 抓取框架,用于抓取 web 站点并从页面中提取结构化的数据。Scrapy 用途广泛,可以用于数据挖掘、监测和自动化测试。本文作者 Erdem İşbilen 为我们演示了如何使用 Python 和 Scrapy 怎样在半个小时内对 10 个在线商店抓取信息。有了 Python 和 Scrapy,我们就可以完成大量的工作,而不需要自己费大力气去开发。

获取启动 App 项目所需的源数据是一步。即便你是全栈开发人员,希望开发一款出色的 Web 应用程序,并能全身心投入到项目中。在编写代码之前,仍然需要一个与领域相关的数据集。这是因为现代应用程序会同时或成批处理大量数据,以便为其用户提供价值。本文,我将解释生成这样一个数据集的工作流程。你将会看到,我在没有任何人工干预的情况下是如何对许多网站进行自动网页抓取的。

我的目标是为价格比较网络应用程序生成一个数据集。我将使用的产品类别以手提袋为例。对于这样的应用,应该每天从不同的在线商店那里收集手提包的产品信息和价格信息。尽管有些在线商店提供了 API 让你访问所需的信息,但并非所有在线商店都会这么做。所以,网页抓取不可避免。

在本文的示例中,我将使用 PythonSparky 为 10 个不同的在线商店生成网络蜘蛛(Web spider)。然后,我将使用 Apache Airflow 自动化这一过程,这样就不需要人工干预来定期执行整个过程。

源代码和现场演示 Web 应用程序

你可以在 GitHub 仓库 找到所有相关的源代码,也可以访问在线 Web 应用程序,使用的是网页抓取项目提供的数据。

在开始任何网页抓取项目之前,必须先定义哪些网站将包含在项目中。我决定抓取 10 个网站,这些网站是土耳其手提包类别中访问量最大的在线商店。

步骤 1:安装 Scrapy 并设置项目文件夹

在创建 Scrapy 蜘蛛之前,必须将 Scrapy 安装到计算机中,并生成 Scrapy 项目。请查看下面的帖子了解更多的信息。

Fuel Up the Deep Learning: Custom Dataset Creation with Web Scraping (推动深度学习:使用网页抓取创建自定义数据集)

 
 

#安装 Scrapy

 

$ pip install scrapy

 

#安装用于下载产品图片的图像

 

$ pip install image

 

#使用 Scrapy 开始网页抓取项目

 

$ scrapy startproject fashionWebScraping

 

$ cd fashionWebScraping

 

$ ls

 

#创建项目文件夹,如下所述

 

$ mkdir csvFiles

 

$ mkdir images_scraped

 

$ mkdir jsonFiles

 

$ mkdir utilityScripts

项目文件夹和文件

项目的文件夹结构

我在本地计算机上创建了一个文件夹结构,将项目文件整齐地放入不同文件夹。csvFiles 文件夹包含了每个被抓取的网站的 CSV 文件。网络蜘蛛将从那些 CSV 文件中读取“起始 URL”来启动网页抓取,因为我不想在网络蜘蛛中对它们进行硬编码。

fashionWebScraping 文件夹包含 Scrapy 蜘蛛和助手脚本,比如 settings.pyitem.pypipelines.py。我们必须修改其中一些 Scrapy 助手脚本,才能成功执行网页抓取过程。

抓取的产品图像将保存在 images_scraped 文件夹中。

在网页抓取过程中,所有的产品信息,如价格、名称、产品链接和图像链接都将存储在 jsonFiles 文件夹中的 JSON 文件中。

  • deldub.py 用于在网页抓取结束后,检测并删除 JSON 文件中重复的产品信息。
  • jsonPrep.py 是另一个实用程序脚本,用于在网页抓取结束后,检测并删除 JSON 文件中的空行项。
  • deleteFiles.py 可删除在上一次网页抓取会话中生成的所有 JSON 文件。
  • jsonToes.py 通过读取 JSON 文件,在远程位置填充 ElasticSearch 集群。这是提供实施全文搜索体验所必需的。
  • sitemap_gen.py 用于生成涵盖所有产品链接的站点地图。

步骤 2:理解特定网站的 URL 结构并为起始 URL 填充 CSV 文件

创建项目文件夹后,下一步就是使用我们要抓取的每个网站的起始 URL 来填充 CSV 文件。几乎所有的电子商务网站都提供分页功能,以便通过产品列表为用户导航。每次导航到下一页时,URL 中的 page 参数都会增加。请参见下面的示例 URL,其中使用了 page 参数。

 
 

https://www.derimod.com.tr/kadin-canta-aksesuar/?page=1

我将使用 {} 占位符,这样就可以通过增加 page 的值来对 URL 进行迭代。我还将使用 CSV 文件中的 gender 列来定义特定 URL 的性别类别。

因此,最终的 CSV 文件看起来如下图所示:

同样的原则,也适用于项目中的其他网站,可以在我的 GitHub 仓库中找到已填充的 CSV 文件。

步骤 3:修改 items.pysettings.py 文件

要开始网页抓取,我们必须修改 items.py 来定义用于存储抓取数据的 item objects

为了定义通用输出数据格式,Scrapy 提供了 Item 类。Item 对象是适用于收集抓取的数据的简单容器。它们提供了一个类似字典的 API,有一个方便的语法来生命它们可用的字段。

引自 scrapy.org

 
 

# fashionWebScraping 文件夹中的 items.py

 

import scrapy

 

from scrapy.item import Item, Field

 

class FashionwebscrapingItem(scrapy.Item):

   
 

#与产品相关的项,如 Id、名称、价格等

 

gender=Field()

 

productId=Field()

 

productName=Field()

 

priceOriginal=Field()

 

priceSale=Field()

 

#要存储链接的项

 

imageLink = Field()

 

productLink=Field()

 

#公司名称项

 

company = Field()

 

pass

 

class ImgData(Item):

 

#用于下载产品图像的图像管道项

 

image_urls=scrapy.Field()

 

images=scrapy.Field()

然后,我们必须修改 settings.py 。这是自定义网络蜘蛛的图像管道和行为所必需的。

通过 Scrapy 设置,你可以自定义所有 Scrapy 组件的行为,包括核心、扩展、管道和网络蜘蛛本身。

引自 scrapy.org

 
 

# fashionWebScraping 文件夹中的 settings.py

 

# fashionWebScraping 项目的 Scrapy 设置

 

# 为简单起见,此文件仅包含被认为重要或常用的设置。你可以参考文档找到更多的设置:

 

# https://doc.scrapy.org/en/latest/topics/settings.html

 

# https://doc.scrapy.org/en/latest/topics/downloader-middleware.html

 

# https://doc.scrapy.org/en/latest/topics/spider-middleware.html

 

BOT_NAME = 'fashionWebScraping'

 

SPIDER_MODULES = ['fashionWebScraping.spiders']

 

NEWSPIDER_MODULE = 'fashionWebScraping.spiders'

 

# 通过在用户代理上标识自己(和你的网站),负责任的抓取。

 

USER_AGENT = 'fashionWebScraping'

 

# 遵守 rebots.txt 规则

 

ROBOTSTXT_OBEY = True

 

# 参阅 https://doc.scrapy.org/en/latest/topics/settings.html

 

# 下载延迟

 

# 另请参阅 autothrottle 的设置和文档

 

# 这样可以避免对服务器造成太大的压力

 

DOWNLOAD_DELAY = 1

 

# 重写默认的请求报头:

 

DEFAULT_REQUEST_HEADERS = {

 

'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

 

'Accept-Language': 'tr',

 

}

 

# 配置项目管道

 

# 参阅 https://doc.scrapy.org/en/latest/topics/item-pipeline.html

 

ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}

 

IMAGES_STORE = '/Users/erdemisbilen/Angular/fashionWebScraping/images_scraped'

item.pysettings.py 对我们项目中的所有网络蜘蛛都有效。

步骤 4:构建网络蜘蛛

Scrapy Spiders 是用于定义如何抓取某个站点(或一组站点)的类,包括如何执行抓取(即跟随链接),以及如何从其页面中提取结构化数据(即抓取项目)。换言之,Spiders 是你定义特定站点(或者在某些情况下为一组站点)的网络蜘蛛和解析页面的自定义行为的地方。

 
 

fashionWebScraping $ scrapy genspider fashionBOYNER boyner.com

在模块中使用 basic 模板创建网络蜘蛛 fashionBOYNERfashionWebScraping.spiders.fashionBOYNER

上面的 shell 命令创建一个空的 spider 文件。让我们将这些代码写入到我们的 fashionBOYNER.py 文件中:

 
 

# fashionWebScraping/Spiders 文件夹中的 'fashionBOYNER.py' 文件

 

# 导入 scrapy 和 scrapy 项

 

import scrapy

 

from fashionWebScraping.items import FashionwebscrapingItem

 

from fashionWebScraping.items import ImgData

 

from scrapy.http import Request

 

# 从 csv 文件读取

 

import csv

 

class FashionboynerSpider(scrapy.Spider):

 

name = 'fashionBOYNER'

 

allowed_domains = ['BOYNER.com']

 

start_urls = ['http://BOYNER.com/']

 

# 此函数通过跟踪 csv 文件中的起始 URL 来帮助我们抓取网站的全部内容

 

def start_requests(self):

 

# 从 csv 文件读取主类别 URL

 

with open ("/Users/erdemisbilen/Angular/fashionWebScraping/

 

csvFiles/SpiderMainCategoryLinksBOYNER.csv", "rU") as f:

   
 

reader=csv.DictReader(f)

 

for row in reader:

 

url=row['url']

 

# 以增量方式更改 page 值来浏览产品列表

 

# 你可以根据最大值来调整产品数量的范围值,默认是抓取 30 个网页

 

link_urls = [url.format(i) for i in range(1,30)]

 

for link_url in link_urls:

 

print(link_url)

 

# 将包含产品的每个链接传递给带有性别元数据的 parse_ product_pages 函数

 

request=Request(link_url, callback=self.parse_product_pages,

 

meta={'gender': row['gender']})

 

yield request

 

# 此函数在 xpath 的帮助下抓取网页数据

 

def parse_product_pages(self,response):

   
 

item=FashionwebscrapingItem()

   
 

# 获取 HTML 块,其中列出了所有的产品。

 

# <div> HTML 元素,具有 "product-list-item" 类名

 

content=response.xpath('//div[starts-with(@class,"product-list-

 

item")]')

 

# 循环遍历内容中的每个 <div> 元素

 

for product_content in content:

 

image_urls = []

 

# 获取产品详细信息并填充项

 

item['productId']=product_content.xpath('.//a/@data

 

-id').extract_first()

 

item['productName']=product_content.xpath('.//img/@title').

 

extract_first()

 

item['priceSale']=product_content.xpath('.//ins[@class=

 

"price-payable"]/text()').extract_first()

 

item['priceOriginal']=product_content.xpath('.//del[@class=

 

"price-psfx"]/text()').extract_first()

 

if item['priceOriginal']==None:

 

item['priceOriginal']=item['priceSale']

 

item['imageLink']=product_content.xpath('.//img/

 

@data-original').extract_first()

   
 

item['productLink']="https://www.boyner.com.tr"+

 

product_content.xpath('.//a/@href').extract_first()

 

image_urls.append(item['imageLink'])

 

item['company']="BOYNER"

 

item['gender']=response.meta['gender']

 

if item['productId']==None:

 

break

 

yield (item)

 

# 下载 image_urls 中包含的图像

 

yield ImgData(image_urls=image_urls)

 

def parse(self, response):

 

pass

我们的 spider 类包含两个函数,即 start_requestsparse_product_pages

start_requests 函数中,我们从已经生成的特定 CSV 文件中读取起始 URL 信息。然后,我们对占位符 {} 进行迭代,将产品页面的 URL 传递给 parse_product_pages 函数。

我们还可以使用 meta={‘gender’: row[‘gender’]} 参数将 gender 元数据传递给 Request 方法中的 parse_product_pages函数。

parse_product_pages 函数中,我们执行实际的网页抓取,并用抓取的数据填充 Scrapy 项。

我使用 Xpath 来定位网页上包含产品信息的 HTML 部分。

下面的第一个 XPath 表达式从当前正在抓取的网页中提取整个产品列表。所有必需的产品信息都包含在内容的 div 元素中。

 
 

# // 从当前节点中选择与所选内容匹配的节点,无论它们在何处

 

#//'//div[starts-with(@class,"product-list-item")]'选择所有的 div 元素,这些元素具有类值 start

 

content = response.xpath('//div[starts-with(@class,"product-list-item")]')

我们需要遍历 content 以获取各个产品,并将它们存储在 Scrapy 项中。借助 XPath 表达式,我们可以很容易地在 content 中找到所需的 HTML 元素。

 
 

# 遍历内容中的每个 <div> 元素

 

for product_content in content:

   
 

image_urls = []

   
 

# 获取产品详细信息并填充项

 

# ('.//a/@data-id') 提取 product_content 中的 <a> 元素的 'data-id' 值

 

item['productId']=product_content.xpath('.//a/@data

 

-id').extract_first()

 

# ('.//img/@title') 提取 product_content 中的 <img> 元素的 `title` 值

 

item['productName']=product_content.xpath('.//img/@title').

 

extract_first()

 

# ('.//ins[@class= "price-payable"]/text()') 提取 <ins> 元素的文本值,该元素具有 product_content 中的 `price-payable` 类别属性。

 

item['priceSale']=product_content.xpath('.//ins[@class=

 

"price-payable"]/text()').extract_first()

 

# ('.//del[@class="price-psfx"]/text()') 提取 <del> 元素的文本值,该元素具有 product_content 中的 `price-psfx` 类属性。

 

item['priceOriginal']=product_content.xpath('.//del[@class=

 

"price-psfx"]/text()').extract_first()

   
 

if item['priceOriginal']==None:

 

item['priceOriginal']=item['priceSale']

   
 

# ('.//img/@data-original') 提取 product_content 中的 <img> 元素的 `data-original` 值

 

item['imageLink']=product_content.xpath('.//img/

 

@data-original').extract_first()

 

# ('.//a/@href') 提取 product_content 中的 <a> 元素的 `href`值

 

item['productLink']="https://www.boyner.com.tr"+

 

product_content.xpath('.//a/@href').extract_first()

   
 

# 将产品图片链接分配到图像管道定义的 `image_urls` 中

 

image_urls.append(item['imageLink'])

   
 

item['company']="BOYNER"

 

item['gender']=response.meta['gender']

   
 

if item['productId']==None:

 

break

   
 

yield (item)

 

**# 下载 image_urls 中包含的图像

 

yield ImgData(image_urls=image_urls)

同样的原则也适用于其他网站。你可以在我的 GitHub 仓库中看到所有 10 个网络蜘蛛的代码。

步骤 5:运行网络蜘蛛并将抓取的数据存储在 JSON 文件中

在抓取过程中,每个产品项都存储在一个 JSON 文件中。每个网站都有一个特定的 JSON 文件,该文件在每次网络蜘蛛抓取时都填充了数据。

 
 

fashionWebScraping $ scrapy crawl -o rawdata_BOYNER.json -t jsonlines fashionBOYNER

JSON 格式相比,使用 jsonlines 格式的内存效率要高得多,特别是当你在一个会话中抓取了大量网页数据时。

注意,JSON 文件名以 rawdata 开头,这表明下一步是在我们的应用程序中使用抓取的原始数据之前,检查并验证它们。

步骤 6:清理并验证 JSON 文件中的抓取数据

在网页抓取过程结束之后,你可能需要先从 JSON 文件中删除一些行项,然后才能在应用程序中使用它们。

JSON 文件中可能有那些带有空字段或重复值的行项。这两种情况,都需要一个修正过程,我使用的是 jsonPrep.pydeldub.py 来处理这两种情况。

jsonPrep.py 查找带有空值的行项,并在检测到它们时将其删除。你可以查看下面的带有解释的示例代码:

 
 

# fashionWebScraping/utilityScripts 文件夹中的 `jsonPrep.py`文件

 

import json

 

import sys

 

from collections import OrderedDict

 

import csv

 

import os

 

# 从 jsonFiles.csv 文件中读取需要验证的所有 json 文件的名称和位置

 

with open("/Users/erdemisbilen/Angular/fashionWebScraping/csvFiles/ jsonFiles.csv", "rU") as f:reader=csv.DictReader(f)

 

# 迭代 jsonFiles.csv 中列出的 json 文件

 

for row in reader:

 

# 从 jsonFiles.csv 文件中读取 jsonFile_raw 列

 

jsonFile=row['jsonFile_raw']

   
 

# 打开 the jsonFile

 

with open(jsonFile) as json_file:

 

data = []

 

i = 0

   
 

seen = OrderedDict()

 

# 对 json 文件中的行进行迭代

 

for d in json_file:

 

seen = json.loads(d)

   
 

# 如果产品 Id 为空,则不包括行项

 

try:

 

if seen["productId"] != None:

 

for key, value in seen.items():

 

print("ok")

 

i = i + 1

 

data.append(json.loads(d))

 

except KeyError:

 

print("nok")

 

print (i)

 

baseFileName=os.path.splitext(jsonFile)[0]

   
 

# 通过从 `file_name_prep` 行读取 filename,将结果写成 json 文件

 

with open('/Users/erdemisbilen/Angular/fashionWebScraping/

 

jsonFiles/'+row['file_name_prep'], 'w') as out:

   
 

json.dump(data, out)

在删除空行项之后,将结果保存到 jsonFiles 项目文件夹中,文件名以 prepdata 开头。

deldub.py 查找重复的行项,并在检测到时将其删除。你可以查看下面的带有解释的示例代码:

 

《使用 Python 和 Scrapy 半小时爬了 10 个在线商店的网页》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. Dropout也能自动化?
  2. div模拟textarea文本域轻松实现高度自适应
  3. c++常见操作的模板
  4. 计算机安全概论论文,计算机安全探讨论文毕业论文(7篇).doc
  5. CVPR2020 Oral|场景去遮挡新方法:港中文首次提出自监督新框架,已开源
  6. join和子查询效率_SparkSQL连接查询中的谓词下推处理(上)
  7. Java基础(1):Java简介和开发环境配置
  8. JSTL和EL的使用
  9. 创建maven web项目无法创建sec目录
  10. cad怎么倒圆角_CAD哪些技巧是菜鸟必备,哪些又让你相见恨晚?
  11. 适合财务人员的财务报表分析软件有哪些?
  12. 2022年网络工程师考试知识点:网络攻击
  13. autojs 复制到粘贴板_JS复制到剪贴板示例代码
  14. 《Python助力交通》公众号说明
  15. DevC++和ege19.01版本图形库的配置安装
  16. ShowWindow与UpdateWindow
  17. gromacs ngmx_GROMACS使用教程
  18. netty_channal学习
  19. 构建单拷贝同源蛋白系统发育树,一条命令提序列!
  20. rdms系统_什么是关系数据库管理系统(RDMS)?

热门文章

  1. 新闻无限分类_新闻传播学考研笔记(9)媒介技术、社会发展与人类传播的演进...
  2. 手把手教你用AirtestIDE无线连接手机
  3. Matlab画常见的信号函数二
  4. java实现手机截屏工具
  5. ElasticSearch学习(三)——Windows集群部署
  6. Redis分布式锁----悲观锁实现,以秒杀系统为例
  7. 销售和程序员哪个好_杀了程序员祭天
  8. C语言预处理指令#error
  9. Lightoj_1422
  10. Android-加载大图,照片墙的实现