爬取表情包
闲来无事,突然想到表情包好久没有更新了,正好这几天学了爬虫,利用爬虫来更新一波表情包,哈哈哈。
有一个网站,叫做“斗图啦”,网址是:https://www.doutula.com/。这里面包含了许许多多的有意思的斗图图片,还蛮好玩的。这里我分析了两种方式获取斗图啦的表情包:
1.利用 Scrapy 框架爬取斗图啦最新套图
2.通过斗图啦网站提供的 API 接口,获取 json 获取图片(异步IO)
说明:本爬虫纯粹是个人娱乐,如果他人使用该爬虫对网站服务器造成不良影响与本人无关。
Scrapy框架爬取套图
页面分析
页面分析是为了找到这些图片或者 URL 之间的规律,通过这些规律我们在写爬虫去获取相应的内容,如图:
通过上图,我们可以发现 “ http://www.doutula.com/article/list/?page=2 ” 中 page 参数表示当前是第几页。套图详情是在 “ http://www.doutula.com/article/detail/xxxxx ” 这个地址下面的,其中 xxxxx 是一个类似套图id的一串数字(因为后面我们是用正则来匹配这串数字,所以具体是多少不重要啦~)。我们要找的表情包就在详情页面下,接着分析详情页面,如图:
由此我们可以发现套图里面的表情包是放在详情页面下有 “class=pic-content” 的 div 标签下的一组 class 属性为 artile_des 的 div 标签里面的,只是它嵌套的比较深是在每个 div 标签下的table>tbody>tr>td>a下。
编写Spider
通过上面的分析我们知道,只需要爬取 “http://www.doutula.com/article/detail/xxxxxx” 和 “ http://www.doutula.com/article/list/?page=x ”这两类页面就可以了。
框架结构:
爬虫--bqbspider.py:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.spider import CrawlSpider, Rule
from scrapy.linkextractor import LinkExtractor
from bqb.items import BqbItem
class BqbspiderSpider(CrawlSpider):
name = 'bqbspider'
allowed_domains = ['www.doutula.com']
start_urls = ['http://www.doutula.com/article/list/']
rules = (
Rule(LinkExtractor(allow=(r'http://www.doutula.com/article/list/\?page=\d+'))),
Rule(LinkExtractor(allow=(r'http://www.doutula.com/article/detail/\d+')), callback='get_image_parse'),
)
def get_image_parse(self, response):
item = BqbItem()
item['title'] = response.xpath('//div[@class="pic-title"]/h1/a/text()').extract_first()
item['data'] = {}
pic_list = response.xpath('//div[@class="pic-content"]//div[@class="artile_des"]')
for pic in pic_list:
pic_url = pic.xpath('./table/tbody/tr[1]/td/a/img/@src').extract_first()
pic_name = pic.xpath('./table/tbody/tr[1]/td/a/img/@alt').extract_first()
item['data'][pic_name] = pic_url
return item
注意:Rule 中定义的规则,会从当前 url 请求的 response 中去进行匹配,如果当前规则匹配成功,并且指定了 callback 回调函数,那么就会请求该 url 并执行回调函数, 如果没有指定回调函数,则表示爬虫继续跟进匹配到的 url。
items.py 文件中定义爬虫返回的字段:
import scrapy
class BqbItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
data = scrapy.Field()
pipelines.py文件中定义了对爬虫爬取到的内容进行的处理操作:
import os
import requests
class BqbPipeline(object):
def process_item(self, item, spider):
dir_name = dict(item)['title']
if not os.path.exists("E:\\images\\" + dir_name):
os.mkdir("E:\\images\\" + dir_name)
data = dict(item)['data']
for pic in data:
pic_url = data[pic]
print(pic_url.rfind('/'))
name = pic_url[pic_url.rfind('/') + 1:]
filename = "E:\\images\\" + dir_name + "\\" + name
try:
with open(filename, 'wb') as f:
f.write(requests.get(data[pic]).content)
except IOError as e:
print(e)
print(name, '下载完成!')
这里爬虫的主要代码就没有了,剩下的就是根据自己的需要配置settings.py文件了,建议设置cookie,和缓存,并配置适当的请求数和延迟。
部分爬取效果截图:
REST接口获取图片
REST接口就简单多了,这是网址提供的请求接口,这就只需要我们构造一个url请求,获取到服务器返回的 json 即可。在返回的 json 中就有表情包的下载链接,直接下载就可以了。啥都不说了,直接上代码:
import re
import requests
import asyncio
# 批量生成 url 请求参数列表
def create_json(key, mine=0):
params = []
for i in range(1, 51):
data = {
'type': 'photo',
'more': 1,
'keyword': key,
'page': i,
'mime': mine,
}
params.append(data)
return params
# 下载并保持图片
@asyncio.coroutine
def save_pic(url):
if re.search(r'^/[a-z].*', url):
url = 'https://www.doutula.com' + url
if url.startswith('//'):
url = 'https:' + url
if re.search(r'jpg|gif|png', url):
b_img = requests.get(url, headers=headers).content
filename = './images/' + url[url.rfind('/') + 1:]
try:
with open(filename, 'wb') as f:
f.write(b_img)
except IOError as e:
print(e)
finally:
pass
# 获取图片url列表
@asyncio.coroutine
def download_img(param):
url = 'https://www.doutula.com/api/search'
img_list = requests.get(url, params=param, headers=headers).json()['data']['list']
while len(img_list) > 0:
img = img_list.pop()
yield from save_pic(img['image_url'])
print('%s下载完成' % img['out_id'])
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' \
' (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36'
}
def main():
loop = asyncio.get_event_loop()
params = create_json('蘑菇头')
tasks = [download_img(param) for param in params]
loop.run_until_complete(asyncio.wait(tasks))
print('Complete!')
if __name__ == '__main__':
main()
获取结果图: