LINUX.ORG.RU

Помогите со скрапингом Youtube

 , ,


2

1

Есть скрипт:

#!/usr/bin/env python3

import requests
from bs4 import BeautifulSoup

f = open('.config/youtube_list.txt')
for line in f:
    r = requests.get(line)
    soup = BeautifulSoup(r.content, 'html.parser')
    ch = soup.select_one('.qualified-channel-title-text').text
    print('Проверка' + '\t' + ch)

Он циклом переходит по ссылкам из файла .config/youtube_list.txt и выводит название канала в выводе. Правда, работает это странным образом. Скрипт может отработать успешно с первого раза (перейти по всем ссылкам и вывести название каналов), а может в любом месте прерваться с ошибкой:

Tomasz ~/» ./.scripts/111.py
Проверка	Канал 1
Проверка	Канал 2
Проверка	Канал 3
Проверка	Канал 4
Traceback (most recent call last):
  File "./.scripts/111.py", line 12, in <module>
    ch = soup.select_one('.qualified-channel-title-text').text
AttributeError: 'NoneType' object has no attribute 'text'

Сейчас приходится перезапускать скрипт несколько раз, чтобы он отработал нормально. Почему это происходит? Что сделано неправильно?

Выхлоп html сохрани, да посмотри, что не правильно.

crutch_master ★★★★★ ()

Так Python пишет в чём ошибка, а она заключается в том, что следующий метод возвращает None, селектор скорее всего не находит этот CSS класс:

soup.select_one('.qualified-channel-title-text')

Тебе надо в этом месте дописать проверку на None, например переписать так:

f = open('.config/youtube_list.txt')
for line in f:
    r = requests.get(line)
    soup = BeautifulSoup(r.content, 'html.parser')
    ch = soup.select_one('.qualified-channel-title-text')
    ch_name = "Не удалось получить канал" if not has_attr('text', ch) else ch.text
    print('Проверка' + '\t' + ch_name)
dva20 ()
Ответ на: комментарий от dva20

Так Python пишет в чём ошибка, а она заключается в том, что следующий метод возвращает None, селектор скорее всего не находит этот CSS класс:

Почему для некоторых ссылок находит, а для некоторых нет? Они что скриптом генерируются на самом сайте? К чему мне тогда привязываться для достижения цели?

Prosto_user ★★★ ()
Ответ на: комментарий от Prosto_user

к ответу выше привязываться а вообще интересный скриптик, как-то я ютуб не пробовал. покручу когда до работки доеду

thymikon ()
Ответ на: комментарий от thymikon

Вот его полная версия, сохраняющая результаты в файл /tmp/youtube.html:

#!/usr/bin/env python3

import requests
from bs4 import BeautifulSoup

url = 'https://www.youtube.com'

stories = []

f = open('.config/youtube_list.txt')
for line in f:
    r = requests.get(line)
    soup = BeautifulSoup(r.content, 'html.parser')
    ch = soup.select_one('.qualified-channel-title-text').text
    name = soup.select_one('.yt-lockup-title a')['title']
    href = soup.select_one('.yt-lockup-title a')['href']
    upload = soup.select('.yt-lockup-meta-info li')[1].text
    print('Проверка' + '\t' + ch)
    stories.append([ch, name, url+href, upload])

string = ''
for stori in stories:
	string += '<li>{0} <a href="{2}"> {1} </a> Загружено: {3} </li>\n'.format(stori[0], stori[1], stori[2], stori[3])

ul = '''<body><ul>
	{}
</ul></body>'''.format(string)

with open('/tmp/youtube.html', 'w', encoding="utf8") as f:
	f.write(ul)

print('Выполнено ✔')
Prosto_user ★★★ ()
Ответ на: комментарий от Prosto_user

Смотри реализацию метода select_one, разбирайся почему он не возвращает объект с атрибутом text, а возвращает None. Там и найдешь ответ.

Они что скриптом генерируются на самом сайте?

😆 Ты не поверишь, весь ютуб нашпигован скриптами и ими одними.

dva20 ()

Вангую, что гугл проверкой плюётся из-за множества запросов.

ИМХО подобное лучше через API делать.

Radjah ★★★★★ ()

у них же апи есть

anonymous ()
Ответ на: комментарий от Radjah

Вангую, что гугл проверкой плюётся из-за множества запросов.

Плюсую, сам писал парсеры ютуба. А если апи не хочется, то лучше парсить старую мобильную версию.

anonymous ()
Ответ на: комментарий от anonymous

Плюсую, сам писал парсеры ютуба.

Правда года два назад. Но сейчас проверил на коленке (да, баш и регулярки, но чтобы просто посмотреть как быстро забанит):

for i in {1..100}; do echo -n "$i => " ; curl -A 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36' -s 'https://m.youtube.com/channel/UCXAeKsZQL7koKko46kfIXXQ' | grep -Po '(?<="channelMetadataRenderer":{"title":")[^"]+' ; done

отдало 100 верных результатов.

anonymous ()
Ответ на: комментарий от anonymous

Вангую, что гугл проверкой плюётся из-за множества запросов.

В этом случае ничего кроме как указать заголовок юзер-агента придумать не могу. Есть что-то еще?

А если апи не хочется, то лучше парсить старую мобильную версию.

Про мобильную версию даже не думал. Она конечно полегче, чем обычная, но в результате все равно возвращается None вместо текста.

Prosto_user ★★★ ()
Ответ на: комментарий от Prosto_user

В этом случае ничего кроме как указать заголовок юзер-агента придумать не могу. Есть что-то еще?

Смешно. Гугл там корячится, подсети банит, рекапчу сует - а ты такой хоп и обманул его сменой юзерагента.

все равно возвращается None вместо текста.

А посмотреть что возвращается с ютуба конечно нельзя, да?

anonymous ()

Ютуб не скрейпил, но по опыту других сайтов. Проблема в том, что ты ожидаешь один формат расположения данных в html, а по факту ютуб тебе иногда выдаёт другой. Ты расчитываешь, что такой-то html элемент есть и пытаешься вытянуть данные из него, а его иногда нет. Делай проверку на NoneType и сохраняй куда-нибудь обрабатываемый html, чтобы понять как данные могут быть предоставлены в нем.

samson_b ()

Scrapy. Без прокси тебя сразу забанят. Делай ротацию прокси и юзер-агентов. Копай в сторону json.

Deleted ()
Последнее исправление: Deleted (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.