Scrapy 提取数据有自己的一套机制,它常被称为选择器(selectors ),其实现方式主要是通过XPath 或者 CSS 表达式来选择 HTML 文件的特定部分。在 Scrapy 框架下写网络爬虫程序,最为关键的工作就是针对目标信息,设计合适的选择器来提取数据。所以,掌握选择器的使用方法至关重要。
01
任务说明
购物网站同类商品有成百上千件,当需要获取这些商品信息进行数据分析与处理时,如果采用人工逐条录入的方式显然效率太低,然而,采用爬虫则可以轻松完成此工作。本项目以京东购物网站为例,将“手表”作为关键词,搜索所有商品,爬取每个条目的基本信息,包含商品名称、价格、图片链接地址等。然后,将这些信息保存为 JSON 类型的文件和 CSV 文件,并将对应图片以其价格命名,保存到当前文件夹中。在本爬虫项目中,用户可以替换搜索关键词“手表”,爬取自己想要的商品信息。
02
任务展示
所爬取的部分商品名称、价格、图片链接信息如图 7-12 所示。
■ 图 7-12  保存在 JSON 文件中的结果数据
下载的部分商品图片如图 7-13 所示。
■ 图 7-13  下载的部分商品图片
03
任务实现
1
分析目标网站
在 Chrome 浏览器中打开京东网站首页( http :// jd.com ),在搜索框中输入关键词“手表”后,查看搜索结果。接下来,分析目标信息在网页源码中的特征。
(1)按键盘上的功能键 F12 ,调出“开发者工具”窗口,再按 Ctrl+Shift+C 组合键,进入“选择元素”模式,将光标移动到第一个商品的“名称”上面,会在开发者工具窗口中显示对应的元素信息。其 title 属性的值就是商品名称,复制该名称。
(2)在网页上右击,选择“查看网页源代码”菜单,按 Ctrl+F 组合键调出“查询框”,粘贴第一步的名称信息,在源码中搜索。由于其中包含较长的 URL 地址信息,为了方便查看关键信息,这里用“占位”两字替换掉这段 URL ,得到如下结果。
(3)从这段源码看出需要的目标信息都在其中。
名称信息:
title="NOMOS 日晷指环黑色限定版
价格信息:
$</em><i>2180.00</i></strong></div>
商品图片:
除此之外,还包含产品详情页信息等。
(4)经抽查验证,确认其他商品同样满足该特征,并且一个页面的所有商品包含在如下标签中。
其中,每一个商品又以如下标签为起始标记。
另外,一个商品包含很多个页面,通过观察,查找各个页面的网址规律。翻到第二页,网址如下。
http
s:
//
search
.jd.
com
/Search?keyword=%E6%
89
%
8
B%E8%A1%A8&enc=utf-
8
&qrst=
1
&rt=
1
&
stop
=

1
&vt=
2
&page=
3
&s=
37
&click=
0
再多翻几页,发现主要的变化规律如下。
①“page=3 ”的值,每翻一页增加 2 。
②“keyword=%E6%89%8B%E8%A1%A8 ”是编码后的十六进制形式,代表的是搜索关键词“手表”,将整个网址复制到浏览器的地址栏中,这段信息会自动转码为汉字“手表”。所以爬取不同商品时,只需要替换该关键词即可。
2
创建 Scrapy 项目
在命令提示符窗口中执行命令:
crapy startproject JdSpider
则在当前目录下自动生成工程文件夹 JdSpider 。
3
创建爬虫文件 *.py
在命令提示符窗口中,切换到工程目录 JdSpider 下,执行命令:
Scrapy genspider jdgw jd.com
则在子目录 JdSpider 中产生爬虫文件 Jdgw.py 。
4
修改 setting.py 文件完成相关环境配置工作
类似项目 7.1 的操作,在 setting.py 文件中修改项目设置,具体内容如下。
# 1.修改http请求头中的USER_AGENT 字段信息
USER_AGENT =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'

# 2.关闭爬虫协议
ROBOTSTXT_OBEY =
False

#3.修改日志等级,不显现过多的过程信息
LOG_LEVEL=
'WARN'

#4 开启两个 pipline
ITEM _ PIPELINES={

' JdSpider.pipelines.JSonPipeline'
:
100
,

' JdSpider.pipelines.CSVPipeline'
:
300
,

5
在 items.py 文件中定义数据字段
在 items.py 文件中,将该项目中要获取的关键信息通过 Field ()方法定义为字段,供项目中其他文件引用。
import
 scrapy



classJdspiderItem(scrapy.Item):
#ID编号
    ID = scrapy.Field()

#商品名称
    P_name=scrapy.Field()

#商品价格
    P_price=scrapy.Field()

# 卖家
    seller = scrapy.Field()

#商品图片url地址
    P_url=scrapy.Field()


pass
6
在爬虫文件 jdgw.py 中写爬虫程序
该爬虫程序起初由 basic 模板自动生成了 JdgwSpider 类的框架,接下来需要用户通过 request和 response 等方式完善其中的数据提取工作。
程序由 start _ requests 函数开始,由于访问京东的商品页涉及翻页的问题,观察网址规律后,给出了网址的通用形式,并赋值给变量 baseurl 。当请求每个具体页面时,用变量 keyword 和 page替换掉字符串中的值。采用 yield 方式,通过 scrapy.Request 方法循环访问每一个页面,并解析数据。
解析每页数据的操作单独写在函数 parse 中,该函数的核心是通过 XPath 选择器和正则表达式提取目标信息,然后通过 yield 将 item 中的信息传送至 pipline 中做进一步处理,最后,通过request.urlretrieve ()方法直接下载商品图片。jdgw.py 文件源代码如下。
import scrapy

from urllib import request

from JdSpider.items import JdspiderItem


#自动新建的爬虫类(继承至Spider类)
classJdgwSpider(scrapy.Spider):
    name =
'jdgw'
    allowed_domains = [
'jd.com'
]


#start_urls = ['https://search.jd.com/Search?keyword="+key+"&pvid=f685b2206cd84bfda3e373b9a4a55f35']
    keyword =
"手表"#要搜索的关键词,可自由修改
    page =
1#访问的页码变量
    count=
0#保存商品图片的编号变量
# 可以通用替换搜索关键字的
    baseurl =
'https://search.jd.com/Search?keyword=%s&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%s&page=%d&s=56&click=0'

defstart_requests(self)
:

# 循环求情多个页面的信息
        print(
"开始...."
)

for
 pa
in
 range(
5
)
:#()中是要请求的页码范围设定
            url=
self
.baseurl % (
self
.keyword,
self
.keyword,
self
.page)
#格式化访问的每个页面地址
yield
 scrapy.Request(url, callback=
self
.parse)

self
.page+=
2

#解析每个页面返回的数据,提取目标字段
defparse(self, response)
:

        bath_info=response.xpath(
'//*[@class="gl-item"]'
)
#bath_info位selector list
#
for
 bi
inbath_info:#可以限定范围[0:10]
            item =JdspiderItem()
# 实例化一个item
            item[
"ID"
]=
self
.count+
1
            item[
"P_name"
] = bi.xpath(
'.//div[@class="p-img"]//@title'
).get()

            item[
"P_price"
]=bi.xpath(
'.//div[@class="p-price"]/strong/i/text()'
).get()

            item[
"seller"
] = bi.xpath(
'.//span[@class="J_im_icon"]/a/@title'
).get()

            item[
"P_url"
] = bi.xpath(
'.//div[@class="p-img"]//img/@source-data-lazy-img'
).get()


self
.count+=
1#计数变量加1
yield
 item


            surl=str(
self
.count)+
"_"
+str(item[
"P_price"
])+
".jpg"#构造保存的图片名称
            p_url=response.urljoin(item[
"P_url"
])

            request.urlretrieve(p_url, surl)
#保存图片
7
修改数据存储和处理程序 piplines.py
piplines.py 文件接收爬虫获得的 item 信息,写了两个 pipline ,一个用于将结果保存为 JSON格式的文件,另一个用于保存为 CSV 格式的文件。
import json

import codecs

import csv

#item结果写入JSon文件
classJSonPipeline(object):
def__init__(self)
:

self
.file = codecs.open(
'goods.json'
,
'wb'
, encoding=
'utf-8'
)


defprocess_item(self, item, spider)
:

        line = json.dumps(dict(item),ensure_ascii=False) +
'\n'
self
.file.write(line)

return
 item

#item结果写入CSV文件
classCSVPipeline(object):
def__init__(self)
:

# 打开文件,指定方式为写,利用第3个参数把csv写数据时产生的空行消除
self
.file = open(
"goods.csv"
,
"a"
, newline=
""
)

# 设置文件第一行的字段名,注意要跟spider传过来的字典key名称相同
self
.fieldnames = [
"ID"
,
"P_name"
,
"P_price"
,
"seller"
,
"P_url"
,]

# 指定文件的写入方式为csv字典写入,参数1为指定具体文件,参数2为指定字段名
self
.writer = csv.DictWriter(
self
.file, fieldnames=
self
.fieldnames)

# 写入第一行字段名,因为只需写入一次,所以放在__init__里面
self
.writer.writeheader()

defprocess_item(self, item, spider)
:

# 写入spider传过来的具体数值
self
.writer.writerow(item)

# 写入完返回
return
 item

defclose(self, spider)
:

self
.file.close()
8
运行爬虫文件
在命令提示符窗口中,输入如下命令运行爬虫,其效果如图 7-12 和图 7-13 所示。当然,也可以按照 7.1.5 节中介绍的方法,建立启动文件,采用直接运行 Python 程序的方式来启动爬虫。
Scrapy crawl jdgw
04
源代码下载
关注微信公众号,后台回复关键词 “商品列表爬取” 即可获得完整源代码。
05
参考书籍
Python边做边学-微课视频版
ISBN:978-7-302-56793-6
陈秀玲 田荣明 冉涌 主编
定价:49.8元
内容简介
本书采用项目化教程的模式,以理论讲解与实战案例演练相结合的方式,以知识点为主线,将每个项目按照知识点拆解分为多个任务,每个任务均以充满趣味性的游戏入手,系统、全面、循序渐进地讲解Python知识点,使读者能够学以致用,融会贯通。全书共分为8个项目,分别是认识新朋友(Python)、开启编程之旅、高级编程之路、叩开面向对象编程之门、异常处理、Python图形界面设计、网络爬虫和使用Python操作数据库。本书的每个知识点都有相应的实现代码,并配有详细的注释说明,便于读者快速理解和掌握。
本书适合零基础的读者,也可作为高等院校的教材,还可供相关领域的广大科研人员、从事大数据分析、数据爬取或深度学习的专业人员等作为参考书使用。
06
精彩推荐
继续阅读
阅读原文