使用scrapy懒加载爬取1688商品图片
说起 scrapy, 很多人应该都不陌生:它是基于 python 的爬虫框架,编程模型十分简单,极易上手,官方文档在此。
我用它爬取过节目音效文件和一些小网站,都十分简单,后来逛淘宝发现其详情页商品图片使用了懒加载技术:只有在浏览器中纵向滚动条滚动到指定的位置时,页面的图片元素才会被动态加载。注意,在加载之前,图片元素不会出现在页面上。它实质上是由用户出发滚动条事件后,JavaScript 自动生成的。
这样的话,使用最原始的解析网页元素的方式不会取得效果,必须想个方法让 scrapy 可以运行 JavaScript。想到了很久以前一个网站自动化测试工具 selenium, 它让我们能够控制浏览器做一些比如滑动滚动条之类的人工操作。
因此,使用 selenium 打开页面并滑动至页面底部,这样商品详情中的图片就会全部加载出来,然后使用 selector/xpath 选取响应的页面元素,并添加 ImagePipeline,最终达到下载商品详情页图片的效果。以下是用到的关键代码。
# 以下代码使用 python 3.6 及 scrapy 1.4.0
class ProductSpider(scrapy.Spider):
name = "Product1688"
start_urls = []
def __init__(self, **kwargs):
# 加载 chrome driver, 它的下载地址位于 https://sites.google.com/a/chromium.org/chromedriver/
super().__init__(**kwargs)
self.driver = webdriver.Chrome('/path/to/your/chromedriver')
self.wait = WebDriverWait(self.driver, 10)
def parse(self, response):
self.driver.get(response.url)
# 打开页面后,滑动至页面底部
self.scroll_until_loaded()
# 以 xpath 寻找商品名(标题 )
title = self.driver.find_element_by_xpath('//*[@id="mod-detail-title"]/h1')
# 以 xpath 寻找商品主图片
main_images_elements = self.driver.find_elements_by_xpath('//*[@id="dt-tab"]/div/ul/li/div/a/img')
# 以 xpath 寻找商品详情图片
detail_images_elements = \
self.driver.find_elements_by_xpath('//*[@id="desc-lazyload-container"]/p/span/strong/img')
item = ProductItem()
main_images = []
detail_images = []
# 获取商品主图的网络地址,针对商品主图片尺寸的特殊处理
for image in main_images_elements:
main_images.append(image.get_attribute('src').replace('60x60.', ''))
# 获取商品详情图片的网络地址
for image in detail_images_elements:
detail_images.append(image.get_attribute('src'))
item['title'] = title.text
item['main_image_count'] = len(main_images)
item['image_urls'] = main_images + detail_images
return item
# 模拟浏览器页面滚到页面底部的行为
def scroll_until_loaded(self):
check_height = self.driver.execute_script("return document.body.scrollHeight;")
while True:
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
try:
self.wait.until(
lambda driver: self.driver.execute_script("return document.body.scrollHeight;") > check_height)
check_height = self.driver.execute_script("return document.body.scrollHeight;")
except TimeoutException:
break
随后添加 ImagePipeline 的代码由于网络上资源众多,就不赘述了。以上代码仅供参考,视具体需求可会有变动,能满足需求就好。
最后附上 Repo 地址