Веб-сканер Python 2 — сканирование изображений пользователей Sina Weibo

Python открытый источник рептилия

Этот блог был впервые опубликован наwww.litreily.top

На самом деле, сканер пользовательских изображений Sina Weibo — это то, чему я научился.pythonЭто первый краулер, который я написал с тех пор, но тогда мне было лень, и я закончил сканировать позже.LofterПозже я счел необходимым подвести итоги, поэтому у меня появился первый блог краулера. Теперь, когда я временно бездействую, я собираюсь компенсировать это от Сины.

Ближе к дому, так как вы выбираете ползать на Sina Weibo, конечно, спрос есть.Это также одна из основных мотиваций для обучения.Да, это Meitu.sinaБольшая часть Weibo пользователя содержит изображения, причем большинство из них являются групповыми изображениями, а одиночных изображений немного.

Во избежание нарушения авторских прав в этой статье используется мой WeibolitreilyВ качестве примера, чтобы проиллюстрировать весь процесс сканирования, хотя изображений мало и качество низкое, схема сканирования абсолютно нормальная, просто измените идентификатор пользователя при ее использовании.

Анализируйте китайские сайты

получить идентификатор пользователя

Прежде чем сканировать, нам нужно знать, что у каждого пользователя есть имя пользователя, а имя пользователя соответствует уникальному целочисленному идентификатору, подобному номеру студента, мой собственный2657006573. Что касается того, как получить идентификатор на основе имени пользователя, есть два метода:

  1. Введите домашнюю страницу пользователя для сканирования, и вы увидите строку данных в адресной строке браузера, то есть идентификатор пользователя.
  2. Ctrl-UПросмотр исходного кода пользователя для сканирования, поиск"uid, Обратите внимание, чтоДвойные кавычки

На самом деле, он может быть автоматически получен сканером, когда известно имя пользователя.uidДа, но я был новичкомpython, и это считается недостаточным, поэтому следующий исходный код принимает идентификатор пользователя в качестве входного параметра.

Анализ параметров хранения изображений

Все картинки пользователя хранятся по такому пути, на самом делевсе изображенияОй! ! !

https://weibo.cn/{uid}/profile?filter={filter_type}&page={page_num}

# example
https://weibo.cn/2657006573/profile?filter=0&page=1
uid: 2657006573
filter_type: 0
page_num: 1

Примечание, даweibo.cnвместоweibo.com, насчет того, как я нашел эту страницу, я, честно говоря, тоже забыл. . .

Ссылка содержит 3 параметра,uid, filter_modeа такжеpage_num. в,uidэто идентификатор пользователя, упомянутый ранее,page_numТак же легко понять, то есть текущее количество страниц для пейджинга увеличивается с 1, то этоfilter_modeЧто тогда?

Не волнуйтесь, давайте сначала посмотрим на страницу↓

filter mode of pictures

Как видите, тип фильтраfilter_modeОтносится к условиям фильтра, всего три:

  1. filter=0 Все микроблоги (включая обычные текстовые микроблоги, перепечатанные микроблоги)
  2. filter=1 Оригинал Weibo (включая простой текст Weibo)
  3. filter=2 Изображение Weibo (должно содержать изображения, включая репринты)

я обычно выбираюоригинальный, потому что я не хочу, чтобы результаты сканирования включали изображения из перепечатанного Weibo. Конечно, вы можете выбрать в соответствии с вашими потребностями.

Анализ цепочки графов

Что ж, источник параметров известен, давайте вернемся и посмотрим на эту страницу. Страница похожа на пустую полку? Никаких следов css, не беда, Сина не собирался активно презентовать эту страницу пользователям. Но для рептилий это отлично, почему вы так говорите? Причины следующие:

  1. Картинки полные, пропусков нет, это визуальная база данных
  2. Меньше стиля, простая страница, экономия трафика, быстрое сканирование
  3. Статические веб-страницы, хранилище подкачки, WYSIWYG
  4. Исходный код содержит все микроблогипервая картинкаа такжеСсылка на групповое фото

Такая веб-страница идеально подходит для практики. Но обратите внимание на пункт 4 выше, чтопервая картинкаа такжеСсылка на групповое фотоНу, это легко понять. Каждый блог может содержать несколько изображений, т.е.Групповое фото, но на странице отображается только первое изображение блога, так называемоепервая картинка,Ссылка на групповое фотоУказывает на URL-адрес, где хранятся все изображения в группе.

Поскольку у меня нет групповых изображений на Weibo, я буду использовать Weibo Лю Ифэй в качестве примера, чтобы проиллюстрировать формат цепочки изображений одиночного изображения и группового изображения.

pictures

Вышеупомянутый Weibo на картинке имеет только одну картинку, вы можете легко получить ссылку на исходную картинку, обратите внимание, чтоисходное изображение, потому что то, что мы видим на странице, — это миниатюра, но, конечно, мы хотим просканироватьисходное изображениеЛа.

В микроблоге под картинкой размещены групповые фотографии, правая часть картинкиChromeСредства разработки могут видеть ссылку на карту группы.

https://weibo.cn/mblog/picAll/FCQefgeAr?rl=2

Откройте ссылку на изображение группы, вы можете увидеть изображение, как показано ниже:

picture's url

Вы можете увидеть ссылку на миниатюру и ссылку на исходное изображение, затем мы нажимаемисходное изображениепосмотри.

picture's origin url

Как видите, ссылка на всплывающую страницу отличается от той, что показана на изображении выше, но очень похожа на ссылку миниатюры на изображении выше. они соответственно:

  1. Миниатюра: http://ww1.sinaimg.cn/thumb180/c260f7ably1fn4vd7ix0qj20rs1aj1kx.jpg
  2. Исходное изображение: http://wx1.sinaimg.cn/large/c260f7ably1fn4vd7ix0qj20rs1aj1kx.jpg

Видно, что толькоthumb180а такжеlargeразница. Теперь, когда шаблон найден, с ним будет намного проще обращаться.Поскольку мы знаем URL миниатюры, мы можем заменить поддомен первого уровня после имени домена наlargeпросто сделай это, не получаяисходное изображениеСсылка снова скачет.

Более того, после многих попыток можно обнаружить, что ссылка на групповую карту и ссылка на миниатюру удовлетворяют регулярному выражению:

# 1. 组图链接:
imglist_reg = r'href="(https://weibo.cn/mblog/picAll/.{9}\?rl=2)"'

# 2. 缩略图
img_reg = r'src="(http://w.{2}\.sinaimg.cn/(.{6,8})/.{32,33}.(jpg|gif))"'

На этом процесс синтаксического анализа Sina Weibo завершен, и формат и метод получения цепочки графов также ясны. Следующее может быть разработано для сканирования.

Определить план сканирования

По результатам анализа легко сформулировать следующую схему обхода:

  1. Учитывая имя пользователя Weibolitreily
  2. Введите домашнюю страницу пользователя для сканирования, вы можете получить ее по URL-адресу.uid: 2657006573
  3. Получить после входа в Weibocookies(В сообщении запроса необходимо использоватьcookies)
  4. Сканируйте https://weibo.cn/2657006573/profile?filter=0&page={1,2,3,...} один за другим
  5. Разберите исходный код каждой страницы, получите ссылку на одно изображение и ссылку на групповое изображение,
  • Одно изображение: прямое получение ссылки на миниатюру изображения;
  • Групповые изображения: просмотрите ссылки на групповые изображения и выполните цикл, чтобы получить ссылки на миниатюры всех изображений на странице группового изображения.
  1. Цикл заменяет ссылку на изображение, полученную на шаге 5, исходной ссылкой на изображение и загружает ее локально.
  2. Повторяйте шаги 4-6, пока не останется картинок.

получить печенье

Для приведенного выше плана есть несколько ключевых моментов, один из которыхcookies, я так и не научился получать его автоматическиcookies, поэтому в настоящее время он получается вручную после входа в Weibo.

get cookies

страница загрузки

На странице загрузки используетсяpython3автономныйurllibБиблиотека, не учился в то времяrequests, может редко использоваться в будущемurllib.

def _get_html(url, headers):
    try:
        req = urllib.request.Request(url, headers = headers)
        page = urllib.request.urlopen(req)
        html = page.read().decode('UTF-8')
    except Exception as e:
        print("get %s failed" % url)
        return None
    return html

Получить путь к хранилищу

Так как яwin10Код написан ниже, но лично предпочитаю использоватьbash, поэтому путь хранения образа имеет следующие два формата:_get_pathФункция автоматически определит тип текущей операционной системы, а затем выберет соответствующий путь.

def _get_path(uid):
    path = {
        'Windows': 'D:/litreily/Pictures/python/sina/' + uid,
        'Linux': '/mnt/d/litreily/Pictures/python/sina/' + uid
    }.get(platform.system())

    if not os.path.isdir(path):
        os.makedirs(path)
    return path

к счастьюwindowsсовместимlinuxСимвол косой черты системы, иначе замена относительного пути в программе довольно хлопотна.

скачать изображение

Благодаря выбранномуurllibбиблиотека, поэтому используйтеurllib.request.urlretrieveохватывать

# image url of one page is saved in imgurls
for img in imgurls:
    imgurl = img[0].replace(img[1], 'large')
    num_imgs += 1
    try:
        urllib.request.urlretrieve(imgurl, '{}/{}.{}'.format(path, num_imgs, img[2]))
        # display the raw url of images
        print('\t%d\t%s' % (num_imgs, imgurl))
    except Exception as e:
        print(str(e))
        print('\t%d\t%s failed' % (num_imgs, imgurl))

исходный код

Другие подробности см. в исходном коде.

#!/usr/bin/python3
# -*- coding:utf-8 -*-
# author: litreily
# date: 2018.02.05
"""Capture pictures from sina-weibo with user_id."""

import re
import os
import platform

import urllib
import urllib.request

from bs4 import BeautifulSoup


def _get_path(uid):
    path = {
        'Windows': 'D:/litreily/Pictures/python/sina/' + uid,
        'Linux': '/mnt/d/litreily/Pictures/python/sina/' + uid
    }.get(platform.system())

    if not os.path.isdir(path):
        os.makedirs(path)
    return path


def _get_html(url, headers):
    try:
        req = urllib.request.Request(url, headers = headers)
        page = urllib.request.urlopen(req)
        html = page.read().decode('UTF-8')
    except Exception as e:
        print("get %s failed" % url)
        return None
    return html


def _capture_images(uid, headers, path):
    filter_mode = 1      # 0-all 1-original 2-pictures
    num_pages = 1
    num_blogs = 0
    num_imgs = 0

    # regular expression of imgList and img
    imglist_reg = r'href="(https://weibo.cn/mblog/picAll/.{9}\?rl=2)"'
    imglist_pattern = re.compile(imglist_reg)
    img_reg = r'src="(http://w.{2}\.sinaimg.cn/(.{6,8})/.{32,33}.(jpg|gif))"'
    img_pattern = re.compile(img_reg)
    
    print('start capture picture of uid:' + uid)
    while True:
        url = 'https://weibo.cn/%s/profile?filter=%s&page=%d' % (uid, filter_mode, num_pages)

        # 1. get html of each page url
        html = _get_html(url, headers)
        
        # 2. parse the html and find all the imgList Url of each page
        soup = BeautifulSoup(html, "html.parser")
        # <div class="c" id="M_G4gb5pY8t"><div>
        blogs = soup.body.find_all(attrs={'id':re.compile(r'^M_')}, recursive=False)
        num_blogs += len(blogs)

        imgurls = []        
        for blog in blogs:
            blog = str(blog)
            imglist_url = imglist_pattern.findall(blog)
            if not imglist_url:
                # 2.1 get img-url from blog that have only one pic
                imgurls += img_pattern.findall(blog)
            else:
                # 2.2 get img-urls from blog that have group pics
                html = _get_html(imglist_url[0], headers)
                imgurls += img_pattern.findall(html)

        if not imgurls:
            print('capture complete!')
            print('captured pages:%d, blogs:%d, imgs:%d' % (num_pages, num_blogs, num_imgs))
            print('directory:' + path)
            break

        # 3. download all the imgs from each imgList
        print('PAGE %d with %d images' % (num_pages, len(imgurls)))
        for img in imgurls:
            imgurl = img[0].replace(img[1], 'large')
            num_imgs += 1
            try:
                urllib.request.urlretrieve(imgurl, '{}/{}.{}'.format(path, num_imgs, img[2]))
                # display the raw url of images
                print('\t%d\t%s' % (num_imgs, imgurl))
            except Exception as e:
                print(str(e))
                print('\t%d\t%s failed' % (num_imgs, imgurl))
        num_pages += 1
        print('')


def main():
    # uids = ['2657006573','2173752092','3261134763','2174219060']
    uid = '2657006573'
    path = _get_path(uid)

    # cookie is form the above url->network->request headers
    cookies = ''
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
            'Cookie': cookies}

    # capture imgs from sina
    _capture_images(uid, headers, path)


if __name__ == '__main__':
    main()

Не забудьте изменить при использованииmainв функцииcookiesа такжеuid!

Тест сканирования

capture litreily

capture litreily end

captured pictures

напиши в конце

  • Краулер был депонирован в проекте с открытым исходным кодомcapturer, добро пожаловать на обмен
  • Поскольку это первый гусеничный робот, многие области нуждаются в улучшении, относительноЛОФТЕР гусеничныйболее опытен в написании
  • В настоящее время на Sina Weibo не обнаружено явных мер против сканирования, но лучше попросить об этом по запросу.