python爬取今日头条专栏_[python3]今日头条图片爬取

前言

代码设计流程,先模拟ajax发送搜索“街拍美女”,提取返回json里面的article_url,再访问article_url,提取article_url响应的图片url,访问图片url并且保存图片。网上也有爬取今日头条图片的案例,但是很多都过时了,为了练习代码,本人亲自写了一个,网上其它案例没有那么复杂,我写的有点复杂化了,不过也比较详细。

获取索引页的链接

常规方法,对此页面发送请求,如对搜索发送请求,分析源代码,查看源代码发现在源代码里面是否有索引的链接,这里 的源代码没有找到链接,因此不能直接request请求此页面。

image

分析AJAX请求

通过滚动页面,发现请求不断增加,在XHR里面也多了一些链接,发现此页面是通过AJAX请求,模拟AJAX请求即可获取索引页的链接。

image

image

由于是json格式,无法直接用BeautifulSoup进行解析,先json.loads()转换为python对象,如下图,为字典对象:

4.png

article_url是在data键的值里面,可通过get("data")获取data键的值,在获取article_url的值,即get("article_url"),这里要对此结果进行分析,才能知道怎么提取里面的article_url,先看在浏览器显示,

如:'user_auth_info': {'auth_info': '广州富储资产管理有限公司融资经理 搞笑领域创作者', 'auth_type': '3', 'other_auth': {'interest': '搞笑领域创作者'}}

浏览器显示如下:

5.png

可以看到,冒号前面为key,后面为value,如果有多个value,则可折叠,这样通过浏览器查看更直观:

6.png

从上图看到,data下面有数字0、1,但数字不是字典的键,通过点击里面article_url链接,发现第一个索引url在5,第二个索引url在6,因此需要for遍历get("data")来获取artilce_url的值,article_url是data的值的键,不是data值嵌套里面的键,因此获取get("data")后,可直接get("article_url")。

7.png

贴上代码,模拟AJAX的请求代码,这里有个小坑,要添加headers的cookie,否则获取会不完整。

模拟AJAX的发送请求代码

def article1(offset):

headers = {

"cookie": "csrftoken=957e945bd7c9021add628d4e07c2f1ad; tt_webid=6690773796519233036; s_v_web_id=2d44edde23ebadf3eb32788eff6cf14a; __tasessionId=xcospbenc1558090235428", #这里有个小坑,要添加headers的cookie,否则获取会不完整

"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",

"referer": "https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D%E7%BE%8E%E5%A5%B3",

"x-requested-with": "XMLHttpRequest",

}

params = {

'aid': 24,

'app_name': 'web_search',

'offset': offset,

'format': 'json',

'keyword': '街拍美女',

'autoload': 'true',

'count': 20,

'en_qc': 1,

'cur_tab': 1,

'from': 'search_tab',

'pd': 'synthesis',

'timestamp': 1557973762622,

}

url = 'https://www.toutiao.com/api/search/content/?'+ urlencode(params)

try:

r = requests.get(url,headers=headers)

if r.status_code == 200:

res=json.loads(r.text)

return res

except RequestException:

print(u'访问错误')

return None

获取article_url代码

def article2(res):

if res and "data" in res.keys():

for a in res.get('data'):

if 'article_url' in a:

art=a.get('share_url')

yield art

获取article_url代码,需要注意,必须用json.loads()对响应进行处理,转换为python对象,这里才可以用get()字典方法提取键的值,用生成器yield来返回所有的article_url,并且用for来打印生成器的返回结果。

获取article_url结果如下:

8.png

请求article_url

def get_detail(url):

headers = {

"cookie": "csrftoken=957e945bd7c9021add628d4e07c2f1ad; tt_webid=6690773796519233036; s_v_web_id=2d44edde23ebadf3eb32788eff6cf14a; __tasessionId=xcospbenc1558090235428",

"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",

}

r = requests.get(url,headers=headers)

if r.status_code==200:

return r.text

分析详情页

获取详情页图片url代码:

def parse_detail(html):

soup=BeautifulSoup(html,'lxml')

title=soup.select('title')[0].get_text()

print(title)

img=re.compile(r'.*?gallery: JSON.parse\("(.*?)\"\)',re.S)

result=re.search(img,html)

if result:

data = json.loads(result.group(1).replace('\\', ''))

if data and "sub_images" in data.keys():

sub=data.get("sub_images")

for imge in sub:

yield imge.get("url") #用return只能获取组图第一张图片,用生成器yield可获取组图全部图片

详情页有两种,一种是组图,一种是综合显示在一个页面,爬取组图吧,下图这种页面。

9.png

详情页图片链接是在源代码里面,所以直接对上述获取到的article_url发送请求,再从响应表获取图片链接,通过正则表达式获取红色方框的内容,即{}以及{}里面的内容,但是正则获取的结果为json格式,需要对结果进行解析,最后提取里面的图片url。

13.png

解析后输出如下图,此时直接提取图片url即可,这里提取图片url和上面提取article_url方法是一样的:

11.png

从上图发现,图片url是在sub_images键的值里,而sub_images的值是在一个列表里面,列表里面有多个字典,for循环遍历每个字典,get("url")即可获取每个字典里的图片url。

打印如下:

12.png

通过上述代码,可以成功爬取到图片,后面的步骤是构造一个offset数组,遍历AJAX请求里面的offset参数,再请求上述获取的图片url,把请求响应写入到文件,图片是二进制,用xxx.content。

保存图片代码

def imges(url):

headers = {

"cookie": "csrftoken=957e945bd7c9021add628d4e07c2f1ad; tt_webid=6690773796519233036; s_v_web_id=2d44edde23ebadf3eb32788eff6cf14a; __tasessionId=xcospbenc1558090235428",

"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",

}

try:

r = requests.get(url,headers=headers)

if r.status_code == 200:

res=r.content

file_path = '{0}/{1}.{2}'.format(os.getcwd(),md5(res).hexdigest(),'jpg')

f=open(file_path,'wb')

f.write(res)

except RequestException:

pass

整体代码:

添加了进程池,并且把生成的数组传递给函数article1()的参数,如果想要获取更多图片,把bb参数值改为更大即可,如把20改为100,如果爬取不了,替换一下headers里面的Cookie,脚本运行之后,会把图片下载到当前目录。

import requests

from urllib.parse import urlencode

import json

from bs4 import BeautifulSoup

from requests.exceptions import RequestException

import re

import os

from hashlib import md5

from multiprocessing.pool import Pool

def article1(offset):

headers = {

"cookie": "csrftoken=957e945bd7c9021add628d4e07c2f1ad; tt_webid=6690773796519233036; s_v_web_id=2d44edde23ebadf3eb32788eff6cf14a; __tasessionId=xcospbenc1558090235428",

"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",

"referer": "https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D%E7%BE%8E%E5%A5%B3",

"x-requested-with": "XMLHttpRequest",

}

params = {

'aid': 24,

'app_name': 'web_search',

'offset': offset,

'format': 'json',

'keyword': '街拍美女',

'autoload': 'true',

'count': 20,

'en_qc': 1,

'cur_tab': 1,

'from': 'search_tab',

'pd': 'synthesis',

'timestamp': 1557973762622,

}

url = 'https://www.toutiao.com/api/search/content/?'+ urlencode(params)

try:

r = requests.get(url,headers=headers)

if r.status_code == 200:

res=json.loads(r.text)

return res

except RequestException:

print(u'访问错误')

return None

def article2(res):

if res and "data" in res.keys():

for a in res.get('data'):

if 'article_url' in a:

art=a.get('share_url')

yield art

def get_detail(url):

headers = {

"cookie": "csrftoken=957e945bd7c9021add628d4e07c2f1ad; tt_webid=6690773796519233036; s_v_web_id=2d44edde23ebadf3eb32788eff6cf14a; __tasessionId=xcospbenc1558090235428",

"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",

}

r = requests.get(url,headers=headers)

if r.status_code==200:

return r.text

def parse_detail(html):

soup=BeautifulSoup(html,'lxml')

title=soup.select('title')[0].get_text()

print(title)

img=re.compile(r'.*?gallery: JSON.parse\("(.*?)\"\)',re.S)

result=re.search(img,html)

if result:

data = json.loads(result.group(1).replace('\\', ''))

if data and "sub_images" in data.keys():

sub=data.get("sub_images")

for imge in sub:

yield imge.get("url")

def imges(url):

headers = {

"cookie": "csrftoken=957e945bd7c9021add628d4e07c2f1ad; tt_webid=6690773796519233036; s_v_web_id=2d44edde23ebadf3eb32788eff6cf14a; __tasessionId=xcospbenc1558090235428",

"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",

}

try:

r = requests.get(url,headers=headers)

if r.status_code == 200:

res=r.content

file_path = '{0}/{1}.{2}'.format(os.getcwd(),md5(res).hexdigest(),'jpg')

f=open(file_path,'wb')

f.write(res)

except RequestException:

pass

def main(offset):

html=article1(offset)

p=article2(html)

for g in p:

h=get_detail(g)

for j in parse_detail(h):

imges(j)

if __name__=='__main__':

aa=0

bb=60

pool = Pool()

num = ([x * 20 for x in range(aa,bb + 1)])

pool.map(main,num)

pool.close()

pool.join()

运行效果:

第一个是未添加进程池,第二个是添加进程池的运行结果。

未添加进程池

添加进程池

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值