Scrapy 爬取今日头条街拍图片

hresh 721 0

Scrapy 爬取今日头条街拍图片

Python爬虫内容都是于2019上半年写的,关于某些网站的爬取技巧可能已经过时了,仅供参考。

之前用 requests 爬取过今日头条街拍的图片,当时只是爬取每篇文章的缩略图,今天尝试用 scrapy 来大规模爬取街拍详细图片。

分析页面

今日头条html页面

今日头条的内容是以 Ajax 加载而成的,我们爬取需要的是的 json 数据而非 html。

今日头条html页面

如上图所示,我们对爬取的 json 数据进行解析,即可得到文章标题,文章详细地址。

    def parse(self, response):
        text = response.text
        json_res = json.loads(text)

        if json_res.get('data'):
            for item in json_res.get('data'):
                # print(item)
                if item.get('cell_type') is not None:
                    continue
                title = item.get('title')
                if "toutiao" in item.get('article_url'):
                    it = ToutiaoJiapaiItem()
                    it['page_url'] = response.url
                    it['article_url'] = item.get('article_url')  # 详细网址
                    it['title'] = title  # 标题

                    yield Request(url=it['article_url'], headers=self.headers, callback=self.parse_page,
                                      meta={'item': it})

由于 json 数据中的图片列表,内容不全且都为缩略图,因此我们继续向下爬取。

分析详情页

在爬取过程中,发现详细页面大概有三种不同的页面展示。

文章中的图片

今日头条文章

这种文章较为普遍,从上往下欣赏图片。当对该类型的页面进行爬取内容时,虽然返回的是 html 内容,但是排版完全乱了。

今日头条文章html

利用正则匹配或者BeautifulSoup提取都比较麻烦,在这里,我采用的是对字符串进行截取处理,提取出有效内容。

        text = response.text
        str_list = text.split('pgc-img')
        if str_list != None:
            for i in range(1, len(str_list)):
                if ""http:" in str_list[i]:
                    pic_url = str_list[i].split('"')[2]
                    pic_list.append(pic_url)

图集

今日头条图集

这种是点击图片滑动欣赏图集,该页面返回的内容与上一种也不相同,因此需要另一种提取方式。

今日头条图集html

我们可以获取 JSON.parse 后的内容,将字符串转换为 json 进行提取。

# 利用正则提取图片地址
        pattern = re.compile('.*?gallery: JSON.parse\("(.*?)\"\)', re.S)
        result = re.search(pattern, text)
        if result:
            data = json.loads(result.group(1).replace('\\', ''))
            if data and 'sub_images' in data.keys():
                sub_images = data.get('sub_images')
                pic_list = [item.get('url') for item in sub_images]

小视频

最后一种是街拍小视频,在这里并没有对视频进行处理。

今日头条视频

最后,我们在 pipelines 文件里对获取到的 url 进行下载。

class ToutiaoJiepaiPipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        for image_url in item['photo_urls']:
            yield Request(image_url)

    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok,x in results if ok]
        if not image_paths:
            raise DropItem('图片未下载好')
        return item

效果如下。

今日头条图片爬取结果

详细代码如下:

import scrapy
import json
from scrapy_phantomjs.items import ToutiaoJiapaiItem
from urllib.parse import urlencode
from scrapy import Request
import re


class ToutiaoJiepaiSpider(scrapy.Spider):
    name = 'toutiao_jiepai'

    headers = {
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36'
    }

    def start_requests(self):
        # yield Request(url='https://www.toutiao.com/a6673629078630695427/', headers=self.headers,
        #               callback=self.parse_page)
        for i in range(0, 2):
            offset = i * 20
            params = {
                'aid': '24',
                'app_name': 'web_search',
                'offset': str(offset),
                'format': 'json',
                'keyword': '街拍',
                'autoload': 'true',
                'count': '20',
                'en_qc': '1',
                'cur_tab': '1',
                'from': 'search_tab',
                'pd': 'synthesis'
            }
            url = 'https://www.toutiao.com/api/search/content/?' + urlencode(params)

            yield Request(url=url, headers=self.headers, callback=self.parse)

    def parse(self, response):
        text = response.text
        json_res = json.loads(text)

        if json_res.get('data'):
            for item in json_res.get('data'):
                if item.get('cell_type') is not None:
                    continue
                title = item.get('title')
                if "toutiao" in item.get('article_url'):
                    it = ToutiaoJiapaiItem()
                    it['page_url'] = response.url
                    it['article_url'] = item.get('article_url')  # 详细网址
                    it['title'] = title  # 标题

                    yield Request(url=it['article_url'], headers=self.headers, callback=self.parse_page,
                                  meta={'item': it})

    def parse_page(self, response):
        item = response.meta['item']
        text = response.text

        pic_list = []

        str_list = text.split('pgc-img')
        if str_list != None:
            for i in range(1, len(str_list)):
                if ""http:" in str_list[i]:
                    pic_url = str_list[i].split('"')[2]
                    pic_list.append(pic_url)

        # 利用正则提取图片地址
        pattern = re.compile('.*?gallery: JSON.parse\("(.*?)\"\)', re.S)
        result = re.search(pattern, text)
        if result:
            data = json.loads(result.group(1).replace('\\', ''))
            if data and 'sub_images' in data.keys():
                sub_images = data.get('sub_images')
                pic_list = [item.get('url') for item in sub_images]
        item['photo_urls'] = pic_list

        yield item

发表评论 取消回复
表情 图片 链接 代码

分享