目标网站分析

1、目标网站分析

进入头条网站,关键字搜索:街拍,出现的页面称为 索引页,如下:

【网络爬虫实战】分析ajax请求并抓取头条街拍美图_网络爬虫

 

上图中的Request URL为请求地址,取出其中的部分信息

https://www.toutiao.com/search_content/?

与下图中的请求头参数拼接,用于构造url.

【网络爬虫实战】分析ajax请求并抓取头条街拍美图_网络爬虫_02

通过不断向下拉动滚动条,发现请求的参数中offset一直在变化(每次增加20),所以每次请求通过offset来控制新的ajax请求。

2、通过ajax请求获取索引页面

'''
获取索引页
'''
def get_page_index(offset, keyword):
    data = {
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab': '1',
        'from': 'search_tab'
    }
    # urlencode()可以将字典对象转化为url的请求参数
    url = 'https://www.toutiao.com/search_content/?' + urlencode(data)
    try:
        response = requests.get(url)
        # print(response.status_code)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        print("请求索引页出错")
        return None

解析ajax请求索引页的返回结果

【网络爬虫实战】分析ajax请求并抓取头条街拍美图_网络爬虫_03

'''
解析ajax请求索引页返回的结果
'''
def parse_page_index(html):
    data = json.loads(html)
    if data and 'data' in data.keys():
        for item in data.get('data'):
            yield item.get('article_url')

基本思路:

  1. 爬取索引页的信息。通过分析ajax请求,得到每个详情页的网址。(比如这里就返回了18个详情页)
  2. 爬取详情页的信息,分析网页代码,用正则表达式得到每个图集的信息。
  3. 将爬取的信息存到MongoDB,并下载图片。
  4. 通过改变offset爬取多页信息,利用多线程加快速度。
流程框架
  • 抓取索引页内容

    利用requests请求目标站点,得到索引网页HTML代码,返回结果。

  • 抓取详情页内容

    解析返回结果,得到详情页的链接,并进一步抓取详情页的信息。

  • 下载图片与保存数据库

    将下载的图片保存到本地,并把页面信息及图片URL保存至MongDB.

  • 开启循环和多线程

    对多页内容遍历,开启多线程提高抓取速度。

 

代码:

# -*- coding: utf-8 -*-
import os
import re
from _md5 import md5
import requests
from urllib.parse import urlencode
from requests.exceptions import RequestException
import json
from multiprocessing import Pool
import pymongo
from bs4 import BeautifulSoup

MONGO_URL = 'localhost'  # 数据库地址
MONGO_DB = 'toutiao'  # 数据库名称
MONGO_TABLE = 'toutiao'  # 数据库表

GROUP_START = 1  # 组图开始组别
GROUP_END = 20  # 组图结束组别
KEYWORD = '街拍'  # 关键词

client = pymongo.MongoClient(MONGO_URL)  # 连接MongoDB
db = client[MONGO_DB]  # 如果已经存在连接,否则创建数据库

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
}

'''
获取索引页
'''


def get_page_index(offset, keyword):
    data = {
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab': '1',
        'from': 'search_tab'
    }
    # urlencode()可以将字典对象转化为url的请求参数, 拼接得到索引页的url
    url = 'https://www.toutiao.com/search_content/?' + urlencode(data)
    try:
        response = requests.get(url, headers=headers)
        # print(response.status_code)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        print("请求索引页出错")
        return None


'''
解析ajax请求索引页返回的结果
'''


def parse_page_index(html):
    data = json.loads(html)  # 反序列化json字符串
    if data and 'data' in data.keys():
        for item in data.get('data'):
            yield item.get('article_url')


'''
获取图片页面详细信息
'''


def get_page_detail(url):
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response.text  # 返回html文本
        return None
    except RequestException:
        print("请求详情页出错", url)
        return None


'''
下载图片
'''


def download_image(url):
    print("正在下载", url)
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            print("即将保存图片")
            save_image(response.content)  # 保存二进制流为图片
        else:
            return None
    except RequestException:
        print("下载图片错误", url)
        return None


'''
保存图片
'''


def save_image(content):
    # if not os.path.exists('images'): # 若不存在图片文件夹则新建
    #     os.mkdir('images')
    # # 第一个参数是当前路径,第二个参数是去除重复图片,第三个参数是图片的格式
    # file_path = '{0}/images/{1}.{2}'.format(os.getcwd(),md5(content).hexdigest(),'jpg')
    # os.getcwd()获取当前文件路径,用md5命名,保证不重复
    file_path = '{}/images/{}.{}'.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()
            print("图片保存成功")
    else:
        print("图片已经存在")

'''
解析详情页面信息
'''


def parse_page_detail(html, url):
    # soup = BeautifulSoup(html, 'lxml')
    # title = soup.select('title')
    # # print(title)
    # if title:
    #     title = title[0].get_text()
    images_pattern = re.compile('articleInfo:.*?title:\'(.*?)\'.*?content.*?\'', re.S)  # .*?匹配任意字符
    result = re.search(images_pattern, html)
    if result:
        title = result.group(1)
        url_pattern = re.compile('"(http:.*?)"')
        image_url = re.findall(url_pattern, str(result.group(2)))
        if image_url:
            for image in image_url:
                download_image(image)  # 下载图片
            data = {
                'title': title,
                'url': url,
                'images': image_url
            }
            return data


'''
将图片存入MONGO数据库
'''


def save_to_mongo(result):
    if result:
        if db[MONGO_TABLE].insert(result):  # 向数据库表中插入数据
            print("存储成功", result)
            return True
    else:
        print("存储失败")
        return False


def main(offset):
    html = get_page_index(offset, KEYWORD)
    # print(html)
    for url in parse_page_index(html):
        html = get_page_detail(url)
        # save_txt(html)  #保存到txt文件中
        print("==================================")
        print(url)
        # print(html)
        if html:
            print("准备解析详细页面")
            result = parse_page_detail(html, url)
            print("即将存储新词到数据库中")
            save_to_mongo(result)
            # print('result = ', result)


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

参考资料:

崔庆才 Python3爬虫

https://www.cnblogs.com/felixwang2/p/8729247.html

https://blog.csdn.net/Cowry5/article/details/79749925