爬取街拍美图(注意:以下长文预警)

成品展示

下图是街拍美图保存到本地的电脑截图。

下图是程序运行时的截图。

需求分析

首先,打开头条的街拍页面,我在不断的往下滑动,页面一直有新的标签刷出来,不过页面的 url 斌并没有变化,所以我猜测这是通过ajax加载的。如下图所示。

上面,我画了三个圈,分别表示三种类型。第一种是点进去之后,必须得通过点击才能看到下一张图片;第二钟是点进去之后,直接往下滑动就可以看到全部的图片,第三种是视频,不在本篇博文的讨论范围。第一和第二种类型都是通过JavaScript加载的页面。

获取索引页

通过查看多个ajxa请求之后,发现只有offset是变化的,并且变化规律很明显。还有,keyword参数其实就是搜索的关键字。

def get_index_page(offset, keyword):params = {'offset': offset,'format': 'json','keyword': keyword,'autoload': 'true','count': '20','cur_tab': '1','from': 'search_tab','pd': 'synthesis'}base_url = 'https://www.toutiao.com/api/search/content/?'url = base_url + urlencode(params)try:response = requests.get(url=url, headers=headers)if response.status_code == codes.ok:print(url + ':导航页请求成功!')return response.textexcept requests.ConnectionError:print('请求导航页失败!')return None

我把参数放在一个字典中,然后用urlencode函数封装属性。然后,拼接两个字符串。

解析索引页内容

分析ajax请求的返回结果,拿到详情页的url。在这里,我发现每个data里面的第一项里面都含有key为cell_type的键值对,而且这个选项没有url,所以这里有两行去除的语句。函数返回的是详情页的url列表。

def parse_index_page(html):data = json.loads(html)if data and 'data' in data.keys():for item in data.get('data'):if item.get('cell_type') is not None:  continueyield item.get('article_url')

根据索引页的url获取详情页

根据URL的链接,逐个请求详情页,并返回详情页的内容。

def get_detail_page(url):try:response = requests.get(url=url, headers=headers)if response.status_code == 200:print(url + ':详情页请求成功!')return response.textelse:return Noneexcept RequestException:print('请求详情页错误', url)return None

解析详情页的内容

通过查看详情页返回的内容可以发现,详情页都是通过JavaScript加载的,而且有三种类型,在下面的函数中,我对其中的两种做了提取工作。第三种没有理会,因为它是视频,不在讨论范围内。

def parse_detail_page(html, url):try:soup = BeautifulSoup(html, 'lxml')title = soup.select('title')[0].get_text()  # 获取文章titleimages_pattern_1 = re.compile('gallery: JSON.parse\("(.*?)"\)', re.S)  # 匹配模式result_1 = re.search(images_pattern_1, html)  # 匹配内容images_pattern_2 = re.compile('img src="(.*?)"', re.S)result_2 = re.findall(images_pattern_2, html) if result_1:  # 第一种网页:点击才能跳转图片str = re.sub(r'(\\)', '', result_1.group(1))  # 去掉url链接中多余的双斜线“\\”if str:  # 如果匹配到内容,执行接下来的操作data = json.loads(str)if data and 'sub_images' in data.keys():  sub_images = data.get('sub_images')images = [item.get('url') for item in sub_images]  # 提取sub_images中图片的url链接yield {'title': title,  # 详情页标题'url': url,  # 详情页链接'images': images  # 图片链接}elif result_2:  # 第二种网页: 往下滑动就能看到全部图片# reulut_2返回的就是一个列表yield {'title': title,  # 详情页标题'url': url,  # 详情页链接'images': result_2  # 图片链接}except:return None  # 跳过异常继续执行

下载图片(下载+存图)

通过URL下载图片,并把二进制文件传到保存图片的函数中。

def download_img(image_url):print('正在下载:', image_url)try:response = requests.get(image_url, headers=headers)if response.status_code == 200:save_to_local_file(response.content)  else:return Noneexcept RequestException:print('下载图片错误:', image_url)return None

存储图片方法

把图片保存到当前文件夹的img文件夹中。

def save_to_local_file(content):img_path = 'img'if not os.path.exists(img_path):os.makedirs(img_path)file_path = img_path + os.path.sep + '{file_name}.{file_suffix}'.format(file_name=md5(content).hexdigest(),file_suffix='jpg')with open(file_path, 'wb') as f:f.write(content)

定义主函数,调用之前的方法

定义一个mian函数,调度上面的所有函数。

def main(offset):html = get_index_page(offset=offset, keyword=keyword)for url in parse_index_page(html):  # 返回的是一个迭代器,每次输出一个网址html = get_detail_page(url)if html:result = parse_detail_page(html, url)  # 传入详情页链接、详情页内容,进行解析print(result)for item in parse_detail_page(html, url):image_list = item.get('images')  # 传入图片链接,下载并保存到本地for image in image_list:download_img(image)

只运行本文件中的主函数

这里是程序的入口,在这里建立了一个进程池,加快爬取的效率。

if __name__ == '__main__':groups = [i * 20 for i in list(range(GROUP_START, GROUP_END))]  pool = Pool()  # 创建进程池pool.map(main, groups) pool.close()pool.join()

全局变量的设置

设置全局变量的意义是实现程序的可配置性。虽然不是全部可配置,但是也算是使得代码更加灵活了以及可重用性更加高了。我在这里设置了偏移量和请求头。

offset = '0'
keyword = '街拍图片'GROUP_START = 1
GROUP_END = 20headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64)","Referer": "https://www.toutiao.com/"
}

总结和提高

这次的爬取代码,我利用课余的时间写了两天,最主要的时间都是耗在程序的架构以及提取上面了。我对正则表达式的掌握程度还不够,不能够熟练的运用。
接下来,我要改进一下保存图片写方式,比如说吧=把图片的链接存储到MySQL数据中,以及在本地保存时要按照每一个详情页一个文件夹这样分开存储,方便我查看图片。

完整代码

__author__ = 'Py.ziMing'
import json
import os
import re
from hashlib import md5
from multiprocessing.pool import Pool
from urllib.parse import urlencodeimport requests
from bs4 import BeautifulSoup
from requests import codes, RequestExceptionoffset = '0'
keyword = '街拍图片'GROUP_START = 1
GROUP_END = 20headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64)","Referer": "https://www.toutiao.com/"
}# 获取索引页:
def get_index_page(offset, keyword):params = {'offset': offset,'format': 'json','keyword': keyword,'autoload': 'true','count': '20','cur_tab': '1','from': 'search_tab','pd': 'synthesis'}base_url = 'https://www.toutiao.com/api/search/content/?'url = base_url + urlencode(params)try:response = requests.get(url=url, headers=headers)if response.status_code == codes.ok:print(url + ':导航页请求成功!')return response.textexcept requests.ConnectionError:print('请求导航页失败!')return None# 解析索引页内容
# 分析ajax请求的返回结果,拿到详情页的url
def parse_index_page(html):data = json.loads(html)if data and 'data' in data.keys():for item in data.get('data'):if item.get('cell_type') is not None:  # 去除没有url的itemcontinueyield item.get('article_url')# 根据索引页的url获取详情页
def get_detail_page(url):try:response = requests.get(url=url, headers=headers)if response.status_code == 200:print(url + ':详情页请求成功!')return response.textelse:return Noneexcept RequestException:print('请求详情页错误', url)return None# 解析详情页的内容
def parse_detail_page(html, url):try:soup = BeautifulSoup(html, 'lxml')title = soup.select('title')[0].get_text()  # 获取文章titleimages_pattern_1 = re.compile('gallery: JSON.parse\("(.*?)"\)', re.S)  # 匹配模式result_1 = re.search(images_pattern_1, html)  # 匹配内容images_pattern_2 = re.compile('img src="(.*?)"', re.S)result_2 = re.findall(images_pattern_2, html)  # 匹配内容if result_1:  # 第一种网页:点击才能跳转图片str = re.sub(r'(\\)', '', result_1.group(1))  # 去掉url链接中多余的双斜线“\\”if str:  # 如果匹配到内容,执行接下来的操作data = json.loads(str)if data and 'sub_images' in data.keys():  # 确保返回的信息中含有sub_images这个信息sub_images = data.get('sub_images')images = [item.get('url') for item in sub_images]  # 提取sub_images中图片的url链接yield {'title': title,  # 详情页标题'url': url,  # 详情页链接'images': images  # 图片链接}elif result_2:  # 第二种网页: 往下滑动就能看到全部图片# reulut_2返回的就是一个列表yield {'title': title,  # 详情页标题'url': url,  # 详情页链接'images': result_2  # 图片链接}except:return None  # 跳过异常继续执行# 下载图片(下载+存图)
def download_img(image_url):print('正在下载:', image_url)try:response = requests.get(image_url, headers=headers)if response.status_code == 200:save_to_local_file(response.content)  # content返回二进制内容,图片一般返回contentelse:return Noneexcept RequestException:print('下载图片错误:', image_url)return None# 存储图片方法
def save_to_local_file(content):img_path = 'img'if not os.path.exists(img_path):os.makedirs(img_path)file_path = img_path + os.path.sep + '{file_name}.{file_suffix}'.format(file_name=md5(content).hexdigest(),file_suffix='jpg')with open(file_path, 'wb') as f:f.write(content)# 定义主函数,调用之前的方法
def main(offset):html = get_index_page(offset=offset, keyword=keyword)for url in parse_index_page(html):  # 返回的是一个迭代器,每次输出一个网址html = get_detail_page(url)if html:result = parse_detail_page(html, url)  # 传入详情页链接、详情页内容,进行解析print(result)for item in parse_detail_page(html, url):image_list = item.get('images')  # 传入图片链接,下载并保存到本地for image in image_list:download_img(image)# 只运行本文件中的主函数
if __name__ == '__main__':groups = [i * 20 for i in list(range(GROUP_START, GROUP_END))]  # python3 range()不能直接生成列表,需要list一下pool = Pool()  # 创建进程池pool.map(main, groups)  # 第一个参数是函数,第二个参数是一个迭代器,将迭代器中的数字作为参数依次传入函数中pool.close()pool.join()

python爬取今日头条街拍美图相关推荐

  1. python爬虫今日头条_python爬虫—分析Ajax请求对json文件爬取今日头条街拍美图

    python爬虫-分析Ajax请求对json文件爬取今日头条街拍美图 前言 本次抓取目标是今日头条的街拍美图,爬取完成之后,将每组图片下载到本地并保存到不同文件夹下.下面通过抓取今日头条街拍美图讲解一 ...

  2. [Python3网络爬虫开发实战] --分析Ajax爬取今日头条街拍美图

    [Python3网络爬虫开发实战] --分析Ajax爬取今日头条街拍美图 学习笔记--爬取今日头条街拍美图 准备工作 抓取分析 实战演练 学习笔记–爬取今日头条街拍美图 尝试通过分析Ajax请求来抓取 ...

  3. python爬取今日头条_Python3网络爬虫实战-36、分析Ajax爬取今日头条街拍美图

    本节我们以今日头条为例来尝试通过分析 Ajax 请求来抓取网页数据的方法,我们这次要抓取的目标是今日头条的街拍美图,抓取完成之后将每组图片分文件夹下载到本地保存下来. 1. 准备工作 在本节开始之前请 ...

  4. 爬取今日头条街拍美图

    相关背景: 本篇文章是基于爬虫实践课程–分析Ajax请求并抓取今日头条街拍美图 其实我最开始也只想在CSDN上面找一篇文章看看结果都是分析没有实操,没办法最后只能自己写了,本篇文章里面的问题也是我遇到 ...

  5. Python爬虫 | 批量爬取今日头条街拍美图

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今日鸡汤浮云一别后,流水十年间. 专栏作者:霖he ...

  6. python爬虫今日头条街拍美图开发背景_【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图...

    [摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...

  7. 转:【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图

    [摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...

  8. Python3网络爬虫开发实战分析Ajax爬取今日头条街拍美图

    本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 很多人学习pyt ...

  9. 【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图

    [摘要] 本节中,我们以今日头条为例来尝试通过分析Ajax请求来抓取网页数据的方法.这次要抓取的目标是今日头条的街拍美图,抓取完成之后,将每组图片分文件夹下载到本地并保存下来. 1. 准备工作 在本节 ...

最新文章

  1. html仿qq最小化怎么实现,JS仿QQ好友列表展开、收缩功能(第一篇)
  2. 原来华为是一家做音乐的公司
  3. 如何卸载eclipse?
  4. 根据数据库中的数据构造treeview
  5. 编译安装Nginx以及配置运行Drupal 8,实现上传进度功能
  6. android-mock-gps
  7. JAVA 进制转换的几个方法
  8. Win7 登录WinXP 共享文件夹,总是提示用户名或密码错误 的解决办法
  9. 广州小程序开发公司哪家好?广州小程序开发公司排行前十?
  10. [翻译]在Windows版或MacOS版的Microsoft Edge上安装一个谷歌浏览器拓展
  11. 人工智能语言--PROLOG
  12. 半年损失超20亿美元,区块链安全赛道被资本疯抢
  13. python 两个等长list的各对应位置元素相加+两个字典相加,相同键元素累加,不同键元素取全集
  14. Python深度优先解决八数码问题
  15. Linux安装redis自启动详解
  16. Java 快速深度克隆对象 [Faster Deep Copies of Java Objects]
  17. 扬州大学计算机控制技术课设,计算机控制技术的课设.doc
  18. JAI LT-400CL相机设置
  19. 2020年ESA中国区10m地表覆盖数据下载
  20. ADC中的LSB误差是什么意思

热门文章

  1. 大学计算机基础课程 研究,非计算机专业“大学计算机基础”课程教学研究
  2. sql文件怎么导入数据库mysql_sql文件怎么用 导入数据库文件
  3. 北大医院马主任谈医疗行业IT运维难题
  4. Python编程基础(快速入门必看)
  5. python队列Queue的详解
  6. JavaScript split()方法
  7. 商家转账到零钱快速开通?额度不够用?提额申请如何怎么才能通过?
  8. 智慧职教云Java题库_智慧职教云课堂2020javaweb基础开发题库及答案
  9. Openstack安装过程及步骤
  10. 你的迷惑,Chia大佬来亲自解开