欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python3 爬取今日头条街拍图片

程序员文章站 2022-05-28 23:31:35
...


import json
from _md5 import md5
from multiprocessing.pool import Pool
import re
import os
import pymongo
import requests
from bs4 import BeautifulSoup
from config import *

# 链接mongodb数据库,多进程这里可能会报警告
client = pymongo.MongoClient(MONGO_URL,connect=False)
# 定义一个数据库
db = client[MONGO_DB]

def get_page_list(offset,keyword):
    '''
       获取主页面所有帖子的链接
    '''
    # 请求ajax的一些参数,通过浏览器F12看到的
    params = {
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab': 1,
        'from': 'search_tab'
    }
    # from urllib.parse import urlencode  # 用下面这种要import这个
    # url解析自带的,就是把那个参数弄成一个链接,教程用的是下面这种方式把参数和url拼接
    # url = 'https://www.toutiao.com/search_content/?' + urlencode(params)

    url = 'https://www.toutiao.com/search_content'
    try:
        # 我是把参数传入到get请求里面,如果用上面那种,这里的params形参就要删掉
        response = requests.get(url,headers = HEADERS,params=params)
        # 因为要返回的是一个文本,所以用response.text,若要返回二进制数据,用response.content
        return response.text
    except:
        # 可能会url请求出错,避免停止,捕获一下异常
        return None

def parse_page_list(html):
    '''
    解析主页,取得帖子的链接
    '''
    # 把得到的ajax弄成一个json,方便处理,另外,注意是loads不是load
    data = json.loads(html)
    # 下面的内容是分析浏览器F12的network中的各种数据得到的
    if data and 'data' in data.keys():
        # 数据是层层字典嵌套的,一步步取出来
        for item in data.get('data'):
            # 这个yield如果不懂可以理解为return的高级版本
            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
        return None
    except:
        return None

def parse_page_detail(html):
    '''
    爬取帖子里面的所以照片链接和帖子名称
    '''
    try:
        # 一开始用的正则,有点小错误,直接拿汤器祭神
        soup = BeautifulSoup(html, 'lxml')
        # 用选择,直接找到项目为title的内容
        title = soup.select('title')[0].get_text()
        # 这是图片的正则模式
        pattern = re.compile('"(http:.*?)"', re.S)
        # 找到所有的图片链接
        images = re.findall(pattern,html)
    except:
        return None
        # 以特定格式返回,title是str,images是list
    return {
        'title':title,
        'images':images
    }

def save_to_mongo(result):
    '''
    存储
    {
        'title':title,
        'images':images
    }
    '''
    # 把结果插入到表MONGO_TABLE中,成功返回True,失败False
    if db[MONGO_TABLE].insert(result):
        print('存到mongodb成功: ',result['title'])
        return True
    return False

def download_save_image(url):
    '''
    下载图片,并保存到本地
    '''
    try:
        # 封装请求
        response = requests.get(url,headers=HEADERS)
        # 如果是图片就返回content好,如果是网页就text
        content = response.content
        # 图片的存路径,因为图片可能会重复,所以加一个md5的命名规则,避免重复下载
        file_path = '{0}/images/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(),'jpg')
        # 我是把图片都放在images文件夹下,如果还要分得更细,可以再创建一个"./images/图片集名字"这样的文件夹
        dir = '{0}/images'.format(os.getcwd())
        # 如果没有images文件夹,就新建一个
        if not os.path.exists(dir):
            os.mkdir(dir)
        # 这个就是创建图片(因为用了md5,所以不允许有重复的图片)
        if not os.path.exists(file_path):
            with open(file_path,'wb') as f:
                f.write(content)
            print('图片保存成功:'+url)
    except:
        return None

def main(offset):
    # 获取主页html
    html = get_page_list(offset,KEYWORD)
    # 解析上面的html得到链接
    url_list = parse_page_list(html)
    for url in url_list:
        # 进入详情页
        html = get_page_detail(url)
        if html:
            # 得到详情页的图片链接
            result = parse_page_detail(html)
            # 如果图片链接结果不为空
            if result and result['images']:
                # 先存相关信息到mongo
                save_to_mongo(result)
                # 再下载一份到本地
                for url in result['images']:
                    download_save_image(url)

if __name__ == '__main__':
    # 三行加起来是并行进行
    l = [i*20 for i in range(START_OFFSET,END_OFFSET)]
    pool = Pool()
    pool.map(main,l)


config.py

# 定义一些变量,等下直接导入就好了
# 下面分别是host,数据库名,表名
MONGO_URL = 'localhost'
MONGO_DB = 'toutiao'
MONGO_TABLE = 'toutiao'
# 起始页面
START_OFFSET = 0
END_OFFSET = 10
# 搜索关键词
KEYWORD = '街拍'
# 假装是浏览器的头
HEADERS = {
     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
     'x-requested-with':'XMLHttpRequest'

}


窗口效果:

Python3 爬取今日头条街拍图片


本地保存:

Python3 爬取今日头条街拍图片


爬虫偶尔能成功,主要好像是因为加了并行,容易被反吧。

容易遇到:

<html><head></head><body></body></html>
<html><head></head><body></body></html>
<html><head></head><body></body></html>
<html><head></head><body></body></html>
<html><head></head><body></body></html>
<html><head></head><body></body></html>
<html><head></head><body></body></html>
若有改进方法,不吝赐教。



相关标签: python3 爬虫