美文网首页C#与.netpython爬虫学习笔记
分析ajax请求,爬取今日头条街拍图片

分析ajax请求,爬取今日头条街拍图片

作者: NewForMe | 来源:发表于2018-07-20 01:02 被阅读0次
    知识点整理:
    • 目录:
      1.分析目标网页代码结构;
      2.代码爬取数据;
      3.保存或下载数据。
    一、分析网页

    我们在头条搜索“街拍”弹的网址https://www.toutiao.com/search/?keyword=街拍,我们按F12查询源码,但很明显这个页面没有我们东西

    image.png
    因此可以想像这个页面内容可能是用ajax加载,点击XHR,看到这里也有一个请求 image.png
    而且这个请求页的内容适好就是这个页面图片内容,所以目标爬取索引页地址就是这个https://www.toutiao.com/search_content/?,请求这个索引页之后就来到图片详情页https://www.toutiao.com/a6579859429166940680/, 同样也是F12打开网页源码,而经过查找看到这些图片的地址是直接藏在网页里面, image.png
    所以我们就可以请求这个页面,然后用正则获取我们详情页的美图地址
    二、写代码爬取

    这里要注意几个点:

    • 1、 请求索引页的时候https://www.toutiao.com/search_content/?这个页面是有参数的, image.png
      因此需要将这些参数urlencode一下然后拼接到请求地址
    • 2、 请求详情页的时候,由于网站做了简单的反爬,需要在请求的时候加入请求头hearders,这样才能获取到刚刚内容
    • 3、 获取到详情页内容,用正则获取到藏有详情页图片地址的json字符串就这样的格式:{\"count\":6,\"sub_images\":[{\"url\":\"http:\\/\\/p9.pstatp.com\\/origin\\/pgc-image\\/.......},反斜杠\有转义效果,因此不能直接用json.loads将字符串转为python对象,直转的话会报错json.decoder.JSONDecodeError,所以我们可能用正则re.sub()或内置函数replace()将字符串替换一下,然后再json.loads()转换
    三、下载保存

    这一步比较简单,就是直接insert 到MongoDB和write一下文件就可以了,直接看代码就能理解

    附上源码:
    • 配置文件---config.py
    MONGO_URL='localhost' #MongoDB本地连接
    MONGO_DB='toutiao' #数据库名
    MONGO_TABLE='toutiao' #表名
    
    GROUP_START=0 #起始
    GROUP_END =20 #结束
    
    KEYWORD = '街拍' #搜索关键词
    
    • 执行代码文件---spider.py
    # -*- coding: utf-8 -*-
    
    import requests
    from requests.exceptions import RequestException
    from urllib.parse import urlencode
    import json
    from bs4 import BeautifulSoup
    import re
    from config import *
    import pymongo
    from hashlib import md5
    import os
    from multiprocessing import Pool
    
    #client = pymongo.MongoClient(MONGO_URL,connect=False)#由于启动多进程爬取,所以与MongoDB数据库连接时可能会报错,就需要加上connect=False,
    client = pymongo.MongoClient(MONGO_URL)#连接MongoDB
    db = client[MONGO_DB]#创建数据库
    
    def get_page_index(offset,keyword):
        data={
            'offset': offset,
            'format': 'json',
            'keyword': keyword,
            'autoload': 'true',
            'count': '20',
            'cur_tab': 3,
            'from':'gallery',
        }
        url='https://www.toutiao.com/search_content/?'+urlencode(data)
        try:
            response = requests.get(url)
            if response.status_code==200:
                return response.text
            return None
        except RequestException:
            print("请求索引页url出错")
            return None
    
    def parse_page_index(html):
        data = json.loads(html)#将json字符串转换成python对象
        if data and 'data'in data.keys():
            for item in data.get('data'):
                yield item.get('article_url')#将方法变成生成器每次返回一个url
    
    def get_page_detail(url):
        headers= {
            'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        }
        try:
            response = requests.get(url,headers=headers)#爬取详情页需要加请求头,不然获取不了数据
            if response.status_code==200:
                return response.text#response.text一般来说这个返回网页源码
            return None
        except RequestException:
            print("请求详情页出错",url)
            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('gallery: JSON.parse\("(.*?)"\),',re.S)#根据数据特点用正则筛选想要的数据
        result = re.search(images_pattern,html)
        if result:
            #print(result.group(1))
            page_detail = result.group(1)
            page_result = page_detail.replace('\\"','"',10000)#这个替换是为了修正数据格式,这样才能用json.loads,否则会报错json.decoder.JSONDecodeError
            last_change = page_result.replace('\\/','/',10000)#这个替换是为了修正详情页图片地址
            #print(page_result)
            data = json.loads(last_change)
            if data and 'sub_images'in data.keys():
                sub_images = data.get('sub_images')
                images = [item.get('url') for item in sub_images]#将详情页图片url循环生成列表
                for image in images: download_image(image)#将详情页图片url循环下载
                return {
                    'title':title,
                    'url':url,
                    'images':images
                }
    
    def save_to_mongo(result):
        if db[MONGO_TABLE].insert(result):#将结果插入到MongoDB
            print('存储到MongoDB成功',result)
            return True
        return False
    
    def download_image(url):
        print("正在下载:",url)
        try:
            response = requests.get(url)
            if response.status_code==200:
                save_image(response.content)##response.content一般来说这个返回文件,图片等二进制内容
            return None
        except RequestException:
            print("下载图片出错",url)
            return None
    
    def save_image(content):
        file_path = '{0}/{1}/{2}.{3}'.format(os.getcwd(),'download_image', 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)
        #print(html)
        for url in parse_page_index(html):
            print(url)
            html =  get_page_detail(url)
            #print(html)
            if html:
                result = parse_page_detail(html,url)
                #print(result)
                save_to_mongo(result)
    
    if __name__ == '__main__':
        #main()
        groups = [x*20 for x in range(GROUP_START,GROUP_END+1)]
        poll = Pool()
        poll.map(main,groups)
    
    
    

    相关文章

      网友评论

        本文标题:分析ajax请求,爬取今日头条街拍图片

        本文链接:https://www.haomeiwen.com/subject/uooxmftx.html