分析Ajax请求并爬取下载今日头条街拍美图

算是跟着来的第二个程序,写的时候也意识到很多问题,可能有一两个地方不了解,就回去翻阅相关资料,慢慢的对最开始的程序也做了一些修正,大体上已经熟悉了一整套爬取流程。
先分析各个部分,最后将总体代码贴上去。

一 索引页源码的获取

#返回索引页源码
def get_page_index(offset,keyword):#处理ajax异步加载的数据。
    data={
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab': 3
    }
    url='https://www.toutiao.com/search_content/?'+urlencode(data)#直接变为url类型网页,处理异步加载的网页
    try:
        response=requests.get(url)
        if response.status_code ==200:
            return response.text
        return None
    except RequestException:
        print('网页打开有误')
        return None

首先分析爬取框架,首先知道所要爬取页面是由Ajax请求决定的,下拉时网址后面的offset值会变化,所以决定了对网页解析时要注意处理这个地方,在这里用到了urllib.parse中的urlencode模块,进行了网址的拼接,使url可以被正常访问,offset留给main函数中去提供。

二 对索引页的解析

def parse_page_index(html):
    try:
        data=json.loads(html)#json格式与python格式互相转化
        if data and 'data'in data.keys():
            for item in data.get('data'):
                yield item.get('article_url')
    except JSONDecodeError:
        pass

因为存在js内容,所以先将源码利用json.loads()方法转换为python形式的数据,观察到在得到的字典中,‘data’键所对应的值中包含了详情页的网址链接,所以利用函数生成器yield方法循环取得详情页的网址,总体循环写在main函数中。

三 详情页源码获取以及解析

def get_page_detail(url):
    try:
        response=requests.get(url)
        if response.status_code ==200:
            return response.text
        return None
    except RequestException:
        print('详情页有误')
        return None

def parse_page_detail(html,url):#解析详情页
    soup=BeautifulSoup(html,'lxml')
    title=soup.select('title')[0].get_text()
    #print(title)
    images_pattern=re.compile(r'gallery: JSON.parse\("(.*?)"\),',re.S)#前面一般加r,避免转义错误
    result=re.search(images_pattern,html)
    if result:#建立在找得到左右划切换图片的网址基础上,否则不返回
        result=re.sub(r'\\','',result.group(1))
        data=json.loads(result)#result为json对象
        if data and 'sub_images' in data.keys():
            sub_images=data.get('sub_images')
            images=[item.get('url') for item in sub_images]#取得一个图片网址列表
            if images:
                for image in images:download_image(image)
                data={
                    'title':title,
                    'url':url,
                    'images':images
                }
                return data
    else:return '0'

源码返回没什么好看的,关键是对详情页图片的提取,经过f12检查后,我们发现所有的图片地址都存放在gallery: JSON.parse()这个括号里面,但是没法简单的提取,这时候我们试图用正则表达式来对图片网址进行提取。注意加上转移符号,对\也进行替换处理,最后将该json对象用loads方式赚啊还,找到’sub_images’,用for循环取得图片网址链表,利用图片下载函数将图片下载到本地,并返回字典,以便于储存在mongodb数据库中。接下来是总体代码。

from urllib.parse import urlencode
from bs4 import  BeautifulSoup
from json.decoder import JSONDecodeError
from requests.exceptions import RequestException
import re
import requests
import pymongo
from config import *
import os
from hashlib import md5
from multiprocessing import  Pool
import json
client=pymongo.MongoClient(MONGO_URL)
ceshi=client['toutiao']
final_data=ceshi['toutiao1']

#返回索引页源码
def get_page_index(offset,keyword):#处理ajax异步加载的数据。
    data={
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab': 3
    }
    url='https://www.toutiao.com/search_content/?'+urlencode(data)#直接变为url类型网页,处理异步加载的网页
    try:
        response=requests.get(url)
        if response.status_code ==200:
            return response.text
        return None
    except RequestException:
        print('网页打开有误')
        return None

#索引页解析,yield返回详情页连接
def parse_page_index(html):
    try:
        data=json.loads(html)#json格式与python格式互相转化
        if data and 'data'in data.keys():
            for item in data.get('data'):
                yield item.get('article_url')
    except JSONDecodeError:
        pass

#返回详情页源码
def get_page_detail(url):
    try:
        response=requests.get(url)
        if response.status_code ==200:
            return response.text
        return None
    except RequestException:
        print('详情页有误')
        return None

def parse_page_detail(html,url):#解析详情页
    soup=BeautifulSoup(html,'lxml')
    title=soup.select('title')[0].get_text()
    #print(title)
    images_pattern=re.compile(r'gallery: JSON.parse\("(.*?)"\),',re.S)#前面一般加r,避免转义错误
    result=re.search(images_pattern,html)
    if result:#建立在找得到左右划切换图片的网址基础上,否则不返回
        result=re.sub(r'\\','',result.group(1))
        data=json.loads(result)#result为json对象
        if data and 'sub_images' in data.keys():
            sub_images=data.get('sub_images')
            images=[item.get('url') for item in sub_images]#取得一个图片网址列表
            if images:
                for image in images:download_image(image)
                data={
                    'title':title,
                    'url':url,
                    'images':images
                }
                return data
    else:return '0'

def save_to_mongo(result):
    if final_data.insert_one(result):
        print('存储成功',result)
        return True
    return False

def download_image(url):
    try:
        response=requests.get(url)
        if response.status_code ==200:
            print('正在下载',url)
            save_image(response.content)#返回二进制,图片用
        return None
    except RequestException:
        print('图片下载错误')
        return None

def save_image(content):
    file_path='{0}/{1}.{2}'.format(os.getcwd(),md5(content).hexdigest(),'jpg')
    if not os.path.exists(file_path):
        with open(file_path,'wb')as f:
            f.write(content)
            f.close()

def main(offset):
    html=get_page_index(offset,KEYWORD)
    for url in parse_page_index(html):#对现在网页上的每个子网页进行遍历,已经开始循环
        page_html=get_page_detail(url)
        if page_html:
            result=parse_page_detail(page_html,url)
            '''if result!='0':
                save_to_mongo(result)'''

if __name__=='__main__':
    groups=[x*20 for x in range(GROUP_START,GROUP_END+1)]
    pool=Pool()
    pool.map(main,groups)

这是配置文件

MONGO_URL='localhost:27017'
GROUP_START = 1
GROUP_END = 19
KEYWORD='街拍'

猜你喜欢

转载自blog.csdn.net/qq_38284204/article/details/78753614