Этот блог был впервые опубликован наwww.litreily.top
На самом деле, сканер пользовательских изображений Sina Weibo — это то, чему я научился.python
Это первый краулер, который я написал с тех пор, но тогда мне было лень, и я закончил сканировать позже.Lofter
Позже я счел необходимым подвести итоги, поэтому у меня появился первый блог краулера. Теперь, когда я временно бездействую, я собираюсь компенсировать это от Сины.
Ближе к дому, так как вы выбираете ползать на Sina Weibo, конечно, спрос есть.Это также одна из основных мотиваций для обучения.Да, это Meitu.sina
Большая часть Weibo пользователя содержит изображения, причем большинство из них являются групповыми изображениями, а одиночных изображений немного.
Во избежание нарушения авторских прав в этой статье используется мой WeibolitreilyВ качестве примера, чтобы проиллюстрировать весь процесс сканирования, хотя изображений мало и качество низкое, схема сканирования абсолютно нормальная, просто измените идентификатор пользователя при ее использовании.
Анализируйте китайские сайты
получить идентификатор пользователя
Прежде чем сканировать, нам нужно знать, что у каждого пользователя есть имя пользователя, а имя пользователя соответствует уникальному целочисленному идентификатору, подобному номеру студента, мой собственный2657006573
. Что касается того, как получить идентификатор на основе имени пользователя, есть два метода:
- Введите домашнюю страницу пользователя для сканирования, и вы увидите строку данных в адресной строке браузера, то есть идентификатор пользователя.
-
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
Относится к условиям фильтра, всего три:
- filter=0 Все микроблоги (включая обычные текстовые микроблоги, перепечатанные микроблоги)
- filter=1 Оригинал Weibo (включая простой текст Weibo)
- filter=2 Изображение Weibo (должно содержать изображения, включая репринты)
я обычно выбираюоригинальный, потому что я не хочу, чтобы результаты сканирования включали изображения из перепечатанного Weibo. Конечно, вы можете выбрать в соответствии с вашими потребностями.
Анализ цепочки графов
Что ж, источник параметров известен, давайте вернемся и посмотрим на эту страницу. Страница похожа на пустую полку? Никаких следов css, не беда, Сина не собирался активно презентовать эту страницу пользователям. Но для рептилий это отлично, почему вы так говорите? Причины следующие:
- Картинки полные, пропусков нет, это визуальная база данных
- Меньше стиля, простая страница, экономия трафика, быстрое сканирование
- Статические веб-страницы, хранилище подкачки, WYSIWYG
- Исходный код содержит все микроблогипервая картинкаа такжеСсылка на групповое фото
Такая веб-страница идеально подходит для практики. Но обратите внимание на пункт 4 выше, чтопервая картинкаа такжеСсылка на групповое фотоНу, это легко понять. Каждый блог может содержать несколько изображений, т.е.Групповое фото, но на странице отображается только первое изображение блога, так называемоепервая картинка,Ссылка на групповое фотоУказывает на URL-адрес, где хранятся все изображения в группе.
Поскольку у меня нет групповых изображений на Weibo, я буду использовать Weibo Лю Ифэй в качестве примера, чтобы проиллюстрировать формат цепочки изображений одиночного изображения и группового изображения.
Вышеупомянутый Weibo на картинке имеет только одну картинку, вы можете легко получить ссылку на исходную картинку, обратите внимание, чтоисходное изображение, потому что то, что мы видим на странице, — это миниатюра, но, конечно, мы хотим просканироватьисходное изображениеЛа.
В микроблоге под картинкой размещены групповые фотографии, правая часть картинкиChrome
Средства разработки могут видеть ссылку на карту группы.
https://weibo.cn/mblog/picAll/FCQefgeAr?rl=2
Откройте ссылку на изображение группы, вы можете увидеть изображение, как показано ниже:
Вы можете увидеть ссылку на миниатюру и ссылку на исходное изображение, затем мы нажимаемисходное изображениепосмотри.
Как видите, ссылка на всплывающую страницу отличается от той, что показана на изображении выше, но очень похожа на ссылку миниатюры на изображении выше. они соответственно:
- Миниатюра: http://ww1.sinaimg.cn/thumb180/c260f7ably1fn4vd7ix0qj20rs1aj1kx.jpg
- Исходное изображение: 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 завершен, и формат и метод получения цепочки графов также ясны. Следующее может быть разработано для сканирования.
Определить план сканирования
По результатам анализа легко сформулировать следующую схему обхода:
- Учитывая имя пользователя Weibo
litreily
- Введите домашнюю страницу пользователя для сканирования, вы можете получить ее по URL-адресу.
uid: 2657006573
- Получить после входа в Weibo
cookies
(В сообщении запроса необходимо использоватьcookies
) - Сканируйте https://weibo.cn/2657006573/profile?filter=0&page={1,2,3,...} один за другим
- Разберите исходный код каждой страницы, получите ссылку на одно изображение и ссылку на групповое изображение,
- Одно изображение: прямое получение ссылки на миниатюру изображения;
- Групповые изображения: просмотрите ссылки на групповые изображения и выполните цикл, чтобы получить ссылки на миниатюры всех изображений на странице группового изображения.
- Цикл заменяет ссылку на изображение, полученную на шаге 5, исходной ссылкой на изображение и загружает ее локально.
- Повторяйте шаги 4-6, пока не останется картинок.
получить печенье
Для приведенного выше плана есть несколько ключевых моментов, один из которыхcookies
, я так и не научился получать его автоматическиcookies
, поэтому в настоящее время он получается вручную после входа в Weibo.
страница загрузки
На странице загрузки используется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
!
Тест сканирования
напиши в конце
- Краулер был депонирован в проекте с открытым исходным кодомcapturer, добро пожаловать на обмен
- Поскольку это первый гусеничный робот, многие области нуждаются в улучшении, относительноЛОФТЕР гусеничныйболее опытен в написании
- В настоящее время на Sina Weibo не обнаружено явных мер против сканирования, но лучше попросить об этом по запросу.