Python爬取今日头条街拍美图
前言
今日我们在 今日头条 网站上爬取 街拍美图 。
今日头条 的数据都是用 Ajax 技术加载渲染完成,打开 今日头条 页面源码,连一根鸡毛都没有。
假如今天生活欺骗了你,不要悲伤,不要哭泣,因为明天生活还会继续欺骗你。
在我们爬虫界,按照 ‘可见即可爬’ 的原则, 所谓的 Ajax 就是 ’换一个页面‘ 爬取我们想要爬取的资源。
换的那个页面,有时是 XHR 文件,有时是 HTML 文件。
目标站点分析
F12审查页面元素。
我们需要的资源全部在这个 URL 下。
获取 JSON 数据
def getPage(offset): params = { 'offset': offset, 'format': 'json', 'keyword': '街拍', 'autoload': 'true', 'count': '20', 'cur_tab': '3', 'from': 'gallery', } url = 'https://www.toutiao.com/search_content/?' + urlencode(params) try: r = requests.get(url) if r.status_code == 200: return r.json() except requests.ConnectionError: return ""
urllib.parse.urlencode()
转换映射对象或两个元素的元组,其可以包含的序列 str 或 bytes 对象。如若是字符串,则结果是由 ‘&’ 分隔的 key=value 的系列队。解析 Json 数据
def getImage(json):
data = json.get('data')
for item in data:
title = item.get('title')
image_list = item.get('image_list')
if image_list:
for item in image_list:
yield{
'title': title,
'image': item.get('url')
}
保存图片
def saveImage(item): img_path = 'img' + os.path.sep + item.get('title') if not os.path.exists(img_path): os.makedirs(img_path) local_image_url = item.get('image') new_image_url = local_image_url.replace('list', 'large') r = requests.get('http:' + new_image_url) if r.status_code == 200: file_path = img_path + os.path.sep +'{0}.{1}'.format(md5(r.content).hexdigest(), 'jpg') if not os.path.exists(file_path): with open(file_path, 'wb') as f: f.write(r.content)
在官方文档中描述 hexdigest() 函数的一段话:
At any point you can ask it for the digest of the concatenation of the strings fed to it so far using the digest() or hexdigest() methods.大概意思是:
到目前为止,hexdigest() 和 digest() 函数能满足你把一串字符串的组合物变成一段摘要的需要。官方文档:
https://docs.python.org/2/library/hashlib.html保存到 MongDB
def saveToMongo(item): if db[MONGO_TABLE].insert(item): print('储存到MONGODB成功', item) return False
主函数和进程池
def main(offset): json = getPage(offset) for item in getImage(json): saveImage(item) saveToMongo(item) if __name__ == '__main__': pool = Pool() groups = [x * 20 for x in range(2)] #爬取五页 pool.map(main, groups) pool.close() #关闭进程池(pool),使其不在接受新的任务 pool.join() #主进程阻塞等待子进程的退出
对 pool 对象调用 join() 方法会让主进程等待所有子进程自行完毕,调用 join() 之前必须先调用 close() ,让其不再接受新的 Process 了。
总结
主要关注如何下载和解析 Json数据。
公众号内输出 ‘崔佬视频’获取崔庆才大佬的《Python3WebSpider》全套视频。
全码
import requests from urllib.parse import urlencode import os from hashlib import md5 import pymongo from multiprocessing.pool import Pool MONGO_URL = 'localhost' MONGO_DB = 'toutiao' MONGO_TABLE = 'toutiao' #数据集合Collection client = pymongo.MongoClient(MONGO_URL) # MongoClient 对象,并且指定连接的 URL 地址 db = client[MONGO_DB] #要创建的数据库名 def getPage(offset): params = { 'offset': offset, 'format': 'json', 'keyword': '街拍', 'autoload': 'true', 'count': '20', 'cur_tab': '3', 'from': 'gallery', } url = 'https://www.toutiao.com/search_content/?' + urlencode(params) try: r = requests.get(url) if r.status_code == 200: return r.json() except requests.ConnectionError: return "" def getImage(json): data = json.get('data') for item in data: title = item.get('title') image_list = item.get('image_list') if image_list: for item in image_list: yield{ 'title': title, 'image': item.get('url') } def saveImage(item): img_path = 'img' + os.path.sep + item.get('title') if not os.path.exists(img_path): os.makedirs(img_path) local_image_url = item.get('image') new_image_url = local_image_url.replace('list', 'large') r = requests.get('http:' + new_image_url) if r.status_code == 200: file_path = img_path + os.path.sep +'{0}.{1}'.format(md5(r.content).hexdigest(), 'jpg') if not os.path.exists(file_path): with open(file_path, 'wb') as f: f.write(r.content) def saveToMongo(item): if db[MONGO_TABLE].insert(item): print('储存到MONGODB成功', item) return False def main(offset): json = getPage(offset) for item in getImage(json): saveImage(item) saveToMongo(item) if __name__ == '__main__': pool = Pool() groups = [x * 20 for x in range(2)] #爬取五页 pool.map(main, groups) pool.close() #关闭进程池(pool),使其不在接受新的任务 pool.join() #主进程阻塞等待子进程的退出