Python 爬虫学习笔记(十(2))scrapy爬取图书电商实战详解
scrapy框架爬取图书电商实战详解,详细步骤
目标是爬取某一系列图书的信息,例如名称、价格、图片等。
一、创建scrapy项目
在PyCharm终端依次输入:
- scrapy startproject dangdang
- cd dangdang\dangdang
- scrapy genspider dang category.dangdang.com/cp01.25.16.00.00.00.html
注:genspider后面的网址不需要带http://和url最后的斜线,如果带上需要去dangdang.py将start_urls手动修改为正确的url。.html的网页末尾一定不要有‘/’
之后测试一下这个url有没有robots协议或者反爬手段等,将parse函数改为输出一行文字,终端scrapy crawl dang发现运行成功,出现这行文字,连君子协议都没有。
二、items.py中定义数据的结构
通俗的说就是定义要下载的数据都有什么
给的案例是name = scrapy.Field(),定义完成的items.py如下
import scrapy
class DangdangItem(scrapy.Item):
src = scrapy.Field() # 图片
name = scrapy.Field() # 书名
price = scrapy.Field() # 价格
三、定位、爬取数据
分析网页源代码,我们需要的数据在如下的标签中
用xpath解析找到三个我们需要的信息
src = //ul[@id="component_59"]//li//img/@src
name = //ul[@id="component_59"]//li//img/@alt
price = //ul[@id="component_59"]//li//p[@class="price"]/span[1]/text()
在这里有个小技巧,所有的selector对象都可以再次调用xpath,所以我们可以这样写:
def parse(self, response):
target_list = response.xpath('//ul[@id="component_59"]//li')
for t in target_list:
src = t.xpath('.//img/@src').extract_first()
name = t.xpath('.//img/@alt').extract_first()
price = t.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
print(src, name, price)
运行测试,暂时的输出结果如下。发现其他正常,图片的src除了第一个都是none.jpg,说明做了反爬(大概率是图片的懒加载)!
分析其余59本书的图片发现,果然src的地址都一样,但当页面滑动到加载那张图片时,图片的src会立马变成data-original里面的地址,这也是懒加载的一种方式。
但还要注意,第一张图片没有做懒加载,所以需要一个判读语句,完整代码如下:
def parse(self, response):
target_list = response.xpath('//ul[@id="component_59"]//li')
for t in target_list:
src = t.xpath('.//img/@data-original').extract_first()
if src is None:
src = t.xpath('.//img/@src').extract_first()
else:
src = src
name = t.xpath('.//img/@alt').extract_first()
price = t.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
print(src, name, price)
四、保存数据到管道
接上,在刚才for循环的最后,首先存到一个book对象里,然后每创建一个就交给管道一个。这里的DangdangItem即在items.py中我们定义数据的结构的地方,导入这个类即可。导入方法为from dangdang.items import DangdangItem 编译器会报错,不用管,没关系!!!
......
name = t.xpath('.//img/@alt').extract_first()
price = t.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
book = DangdangItem(src=src, name=name, price=price)
# 获取一个book就将一个book交给pipelines
yield book
之后我们需要用到管道,但必须要先在settings.py中设置开启管道,将settings中的如下代码行解开注释即可(这里的300代表优先级,优先级数值范围是1-1000,值越低优先级越高)。
ITEM_PIPELINES = {
'dangdang.pipelines.DangdangPipeline': 300,
}
最后在pipelines.py中保存爬取数据,这里有两种写法。
首先第一种,因为yield是迭代器,产生一个book就交到pipelines执行一次process_item函数,所以文件会好多次被打开。
- 这里的写入模式我们不能用’w’,否则会被覆盖。
- write方法必须要写入str字符串类型,所以要强转
class DangdangPipeline:
# item就是yield传来的book对象
def process_item(self, item, spider):
with open('book.json', 'a', encoding='utf-8') as f:
f.write(str(item))
return item
第二种,借助open_spider()和close_spider()函数。仅打开一次文件,保持打开状态,每个book不断写入,最后关闭文件。
class DangdangPipeline:
# 在爬虫文件执行开始前,执行的函数
def open_spider(self, spider):
self.f = open('book.json', 'w', encoding='utf-8')
# item就是yield传来的book对象
def process_item(self, item, spider):
self.f.write(str(item))
return item
# 在爬虫文件执行完后,执行的函数
def close_spider(self, spider):
self.f.close()
推荐第二种方式,因为第一种方式对文件的操作过于频繁,需要开关文件次数太多。
五、多条管道下载
只需模仿DangdangPipeline类另写一个类,再在settings.py中开启管道即可
import urllib.request
class DangdangDownloadPipeline:
def process_item(self, item, spider):
url = 'http:' + item.get('src')
filename = './books/' + item.get('name') + '.jpg'
urllib.request.urlretrieve(url=url, filename=filename)
return item
ITEM_PIPELINES = {
'dangdang.pipelines.DangdangPipeline': 300,
'dangdang.pipelines.DangdangDownloadPipeline': 301
}
六、多页数据的下载
分析第一页、第二页、第三页的url发现很相似,只是pg后面的数字在改变
我们的代码逻辑没有变,只有url在改变
所以我们需要改变页码,然后不断回调parse函数执行相同的逻辑。dang.py完整代码如下:
import scrapy
from dangdang.items import DangdangItem
class DangSpider(scrapy.Spider):
name = 'dang'
# 如果多页下载,需要调整allowed_domains的范围,一般情况下只写域名
allowed_domains = ['category.dangdang.com']
start_urls = ['http://category.dangdang.com/cp01.25.16.00.00.00.html']
base_url = 'http://category.dangdang.com/pg'
page = 1
def parse(self, response):
target_list = response.xpath('//ul[@id="component_59"]//li')
for t in target_list:
src = t.xpath('.//img/@data-original').extract_first()
if src is None:
src = t.xpath('.//img/@src').extract_first()
else:
src = src
name = t.xpath('.//img/@alt').extract_first()
price = t.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
book = DangdangItem(src=src, name=name, price=price)
# 获取一个book就将一个book交给pipelines
yield book
if self.page < 100:
self.page = self.page + 1
url = self.base_url + str(self.page) + '-cp01.25.16.00.00.00.html'
# 调用parse方法
# scrapy.Request就是scrapy的get请求, url是请求地址,callback是要执行的函数
yield scrapy.Request(url=url, callback=self.parse)
yield可以让parse函数不断在回调自己。
七、其他代码
Items.py
import scrapy
class DangdangItem(scrapy.Item):
src = scrapy.Field() # 图片
name = scrapy.Field() # 书名
price = scrapy.Field() # 价格
pipelines.py
from itemadapter import ItemAdapter
class DangdangPipeline:
# 在爬虫文件执行开始前,执行的函数
def open_spider(self, spider):
self.f = open('book.json', 'w', encoding='utf-8')
# item就是yield传来的book对象
def process_item(self, item, spider):
self.f.write(str(item))
return item
# 在爬虫文件执行完后,执行的函数
def close_spider(self, spider):
self.f.close()
import urllib.request
class DangdangDownloadPipeline:
def process_item(self, item, spider):
url = 'http:' + item.get('src')
filename = './books/' + item.get('name') + '.jpg'
urllib.request.urlretrieve(url=url, filename=filename)
return item
步骤总结
- 创建scrapy项目
- 在items.py中定义所需数据的结构
- 利用response.xpath定位爬取数据
- 利用管道下载数据:首先创建一个要存储的对象book = DangdangItem(src=src, name=name, price=price)。然后用yield将其逐个交给pipelines。然后在settings.py里开启管道。pipelines.py中的process_item函数中的item就是这里的book对象。在这里面操作并存储数据。
- 多管道下载只需在pipelines.py中再定义一个类,同样也需要开启管道。
- 下载多页数据需要先设置一个页码变量,在parse函数中回调自己,过程中页码加一。需要用到yield关键字和scrapy.Request(url=url, callback=self.parse),即scrapy的get请求,callback是回调的函数名,不需要加括号。
更多推荐
所有评论(0)