парсинг, не срабатывает выбор тега

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
добрый день. не могу понять, где я ошибаюсь

сайт sympaty.net/20160613/chto-delat-posle-solyariya/
requests+BeautifulSoup

Python:
tags_list = soup.find_all('div')
print('tags_list ', len(tags_list))  # 116 тегов
for tag in tags_list:
    if len(tag.find_all('p')) > 5:
        article_tag = tag

div, у которых тегов p больше 5 хватает, но код не срабатывает
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
div, у которых тегов p больше 5 хватает, но код не срабатывает
Код записывает тег у которого больше 5 дочерних тегов p в переменную article_tag.
Это можно проверить выводом в консоль значения переменной после каждой ее перезаписи:
Python:
import requests
from bs4 import BeautifulSoup


response = requests.get('https://www.sympaty.net/20160613/chto-delat-posle-solyariya/')
html = response.content.decode('utf-8')
soup = BeautifulSoup(html, 'lxml')

tags_list = soup.find_all('div')
print('tags_list ', len(tags_list))  # 116 тегов
for tag in tags_list:
    if len(tag.find_all('p')) > 5:
        article_tag = tag
        print(str(article_tag)[:100])  # выводим первые 100 символов значения переменной
        print('-' * 50)
 
  • Мне нравится
Реакции: robisho

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
получается, что при выполнении этого куска происходит ошибка.
Ошибка возникает потому, что переменная article_tags_list определена в блоке try/except и в случае возникновения исключения получается не определенной.
Чтобы исправить - можно определить переменную вне блока try/except.
Python:
...
article_tags_list = []
article_tag = ''
for tag in tags_list:
    if len(tag.find_all("p")) > 5:
        article_tag = tag
        try:
            article_tags_list = article_tag.find_all(
                ["h2", "h3", "h4", "p", "ul", "ol", "figure"]
            )
            print(article_tag.name, article_tag.attrs)
        except:
            print("Тег статьи не определен!")
        ...
 
  • Мне нравится
Реакции: robisho

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
if 'post-page' in div.attrs['class']:
KeyError: 'class'
Ошибка возникает из-за отсутствия ключа class в словаре div.attrs.
Чтобы исправить можно использовать метод get() с указанием значения по умолчанию:
Python:
tag1 = soup.find('article')
tag2 = soup.find('main', {'class': True})


div_list = soup.find_all('div')
article_tag = ''

if tag1:
    if len(tag1.find_all('p')) > 4:
        article_tag = tag1
elif tag2:
    if len(tag2.find_all('p')) > 4:
        article_tag = tag2
else:
    for div in div_list:
        if len(div.find_all('p')) > 5:
            if 'post-page' in div.attrs.get('class', []):
                article_tag = div
            elif str(re.compile('content')) in div.attrs.get('id', []):
                article_tag = div
            elif 'http://schema.org/Article' in div.attrs.get('itemtype', []):
                article_tag = div
            elif str(re.compile('article')) in div.attrs.get('class', []):
                article_tag = div
            elif str(re.compile('[cC]ontent')) in div.attrs.get('class', []):
                article_tag = div
            elif str(re.compile('container')) in div.attrs.get('class', []):
                article_tag = div
            elif str(re.compile('entry')) in div.attrs.get('class', []):
                article_tag = div

print(article_tag)
 
  • Мне нравится
Реакции: robisho

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
в чем подвох? вроде бы и там и там строка сравнивается
re.compile('article') - это не строка, а <class 're.Pattern'>
Увидеть разницу можно с помощью print():
Python:
print(re.compile('article'))  # re.compile('article')
print(re.compile('article').pattern)  # article 
print('article')  # article
 
  • Мне нравится
Реакции: robisho

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
bipbap.ru/krasivye-kartinki/kartinki-krasivye-s-dnem-rozhdeniya-sveta-35-foto.html
может не совсем удачный урл, но в принципе все нужности есть
посмотрел ссылку - там нет тега article или div с классом article
при выполнение кода срабатывает условие
Python:
elif tag3:
но не срабатывает
Python:
if len(tag3.find_all('p')) > 5:
так как в данном теге нет контента и пяти тегов p
else тоже не срабатывает, так как уже сработал elif, но даже если закоментировать elif чтобы сработал else - ни одно условие из него не срабатывает
исправить можно так:
- закоментировать elif tag3:
- заменить условие
Python:
elif re.compile('main').pattern in div.attrs['class']:
на такое
Python:
elif re.search('main', ' '.join(div.attrs['class'])):
 
  • Мне нравится
Реакции: robisho

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
так работает, но есть еще пару атрибутов класса, например, comment, которые тоже нужно удалить. как это можно осуществить
Добавить условие в паттерн например:
Python:
else:
    for div in div_list:
        if len(div.find_all('p')) > 5:
            if not re.search('footer|content', ' '.join(div.attrs.get('class', []))):
                print(div.name, div.attrs)
                print('-' * 10)
                ......
 
  • Мне нравится
Реакции: robisho

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
Код записывает тег у которого больше 5 дочерних тегов p в переменную article_tag.
Это можно проверить выводом в консоль значения переменной после каждой ее перезаписи:
Python:
import requests
from bs4 import BeautifulSoup


response = requests.get('https://www.sympaty.net/20160613/chto-delat-posle-solyariya/')
html = response.content.decode('utf-8')
soup = BeautifulSoup(html, 'lxml')

tags_list = soup.find_all('div')
print('tags_list ', len(tags_list))  # 116 тегов
for tag in tags_list:
    if len(tag.find_all('p')) > 5:
        article_tag = tag
        print(str(article_tag)[:100])  # выводим первые 100 символов значения переменной
        print('-' * 50)
прошу простить, что я выложил только куцый кусок кода, но мне казалось, что дело именно в этом фрагменте
на самом деле после этого цикла есть такое продолжение
Python:
try:
    article_tags_list = article_tag.find_all(['h2', 'h3', 'h4', 'p', 'ul', 'ol', 'figure'])
    print(article_tag.name, article_tag.attrs)
except:
    print('Тег статьи не определен!')


article = ''

for t in article_tags_list:
    try:
        if t.name == 'p':
            if len(t.text.strip()) != 0:
                article += '\n' + f'<{t.name}>'+ t.text.strip() + f'</{t.name}>' + '\n'
            if t.find('img') is not None:
                article += '\n' + str(t.find('img')) + '\n'

        elif t.name == 'ul' or t.name == 'ol':
            if t.find_all('li') is not None:

                for l in t.find_all('li'):

                    article += f'<{l.name}>'+ l.text.strip() + f'</{l.name}>' + '\n'

        elif t.name == 'figure':
            if t.find('img') is not None:
                article += '\n' + str(t.find('img')) + '\n'

        else:
            article += '\n' + f'<{t.name}>'+ t.text.strip() + f'</{t.name}>' + '\n'
    except Exception as e:
            print(e, type(e))
            print('Ошибка в получении статьи ')
            continue

получается, что при выполнении этого куска происходит ошибка.
Код:
Traceback (most recent call last):
  File "test_new.py", line 310, in <module>
    get_article(session, headers, url)
  File "test_new.py", line 144, in get_article
    for t in article_tags_list:
UnboundLocalError: local variable 'article_tags_list' referenced before assignment

т.е. идея была в том, что сначала из общего количества дивов отбираем тех, у которых есть больше 5 тегов р, а потом из этих дивов нужно выбрать один, чтобы в нем были ['h2', 'h3', 'h4', 'p', 'ul', 'ol', 'figure'], это будет нужная статья.
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
Ошибка возникает потому, что переменная article_tags_list определена в блоке try/except и в случае возникновения исключения получается не определенной.
Чтобы исправить - можно определить переменную вне блока try/except.

большое спасибо) теперь осталось выбрать тег, самый близкий к статье)

есть такая болванка
Python:
tag1 = tag.find('article')
tag2 = tag.find('div', itemtype='http://schema.org/Article')
tag3 = tag.find('div', id=re.compile('content'))
tag4 = tag.find('div', class_='post-page')
tag5 = tag.find('main', {'class': True})
tag6 = tag.find('div', class_=re.compile('article'))
tag7 = tag.find('section')
tag8 = tag.find('div', class_=re.compile('[cC]ontent'))
tag9 = tag.find('div', class_=re.compile('entry'))
tag10 = tag.find('div', class_=re.compile('container'))

это теги, с которых обычно(ну то что я смог вычленить) начинается статья. можно перебрать все через if-elif после
if len(tag.find_all("p")) > 5:
Python:
if len(tag.find_all("p")) > 5:
    if tag.find('article'):
        article_tag = tag
    
    elif tag.find('div', itemtype='http://schema.org/Article'):
        article_tag = tag
    elif....

но может есть какой-то вариант по-изящнее?
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
это теги, с которых обычно(ну то что я смог вычленить) начинается статья. можно перебрать все через if-elif после
Этот способ не вернет нужный тег, так как в главном теге страницы <div id="page"> содержится нужный тег и при срабатывание одного из условий в переменную article_tag будет присвоен главный тег.
То есть этим способом можно получить только тег внутри которого есть нужный тег.
но может есть какой-то вариант по-изящнее?
Можно вместо if/else сделать список с функциями:
Python:
test_tag = [
    lambda tag: tag.find('article'),
    lambda tag: tag.find('div', itemtype='http://schema.org/Article'),
    lambda tag: tag.find('div', id=re.compile('content')),
    lambda tag: tag.find('div', class_='post-page'),
    lambda tag: tag.find('main', {'class': True}),
    lambda tag: tag.find('div', class_=re.compile('article')),
    lambda tag: tag.find('section'),
    lambda tag: tag.find('div', class_=re.compile('[cC]ontent')),
    lambda tag: tag.find('div', class_=re.compile('entry')),
    lambda tag: tag.find('div', class_=re.compile('container')),
]
и проверять условие так:
Python:
for func in test_tag:
   if func(tag):
       article_tag = tag
       break
но это только чтобы if/else не писать - проблему поиска тега это не решает.
 
  • Мне нравится
Реакции: robisho

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
Python:
tag1 = soup.find('article')
tag2 = soup.find('main', {'class': True})

div_list = soup.find_all('div')
#print('div_list ', len(div_list))

if tag1:
    if len(tag1.find_all('p')) > 4:
        article_tag = tag1
elif tag2:
    if len(tag2.find_all('p')) > 4:
        article_tag = tag2
else:
    for div in div_list:
        if len(div.find_all('p')) > 5:
            if 'post-page' in div.attrs['class']:
                article_tag = div
            elif str(re.compile('content')) in div.attrs['id']:
                article_tag = div
            # elif 'http://schema.org/Article' in div.attrs['itemtype']: # здесь на itemtype ругается
            #     article_tag = tag
            elif str(re.compile('article')) in div.attrs['class']:
                article_tag = div
            elif str(re.compile('[cC]ontent')) in div.attrs['class']:
                article_tag = div
            elif str(re.compile('container')) in div.attrs['class']:
                article_tag = div
            elif str(re.compile('entry')) in div.attrs['class']:
                article_tag = div

article_tags_list = []
try:
    article_tags_list = article_tag.find_all(['h2', 'h3', 'h4', 'p', 'ul', 'ol', 'figure'])
    print(article_tag.name, article_tag.attrs)
except:
    print('Тег статьи не определен!')

такая конструкция( if 'string' in tag.attrs['class'] ) применялась в подобной ситуации, но здесь выдает
Traceback (most recent call last):
File "test3.py", line 173, in <module>
get_article(session, headers, url)
File "test3.py", line 96, in get_article
if 'post-page' in div.attrs['class']:
KeyError: 'class'

хотя div.attrs['id'] пропускает норм
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
спасибо за помощь) вот интересно, так не срабатывает поиск тега
Python:
elif str(re.compile('article')) in div.attrs.get('class', []):
    article_tag = div

а вот так работает

Python:
elif 'article' in div.attrs.get('class', []):
    article_tag = div

в чем подвох? вроде бы и там и там строка сравнивается
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
re.compile('article') - это не строка, а <class 're.Pattern'>
Увидеть разницу можно с помощью print():
Python:
print(re.compile('article'))  # re.compile('article')
print(re.compile('article').pattern)  # article
print('article')  # article

переписал цикл for, но возникла небольшая проблема

Python:
for div in div_list:
    if len(div.find_all('p')) > 5:
        if "articleBody" in div.attrs.get('itemprop', []):
            article_tag = div
        elif 'http://schema.org/Article' in div.attrs.get('itemtype', []):
            article_tag = div

        elif div.has_attr('id'):
            if re.compile('content').pattern in div.attrs['id']:
                article_tag = div
            elif re.compile('article').pattern in div.attrs['id']:
                article_tag = div

        elif div.has_attr('class'):
            # print(type(div.attrs['class']))
            if re.compile('article') in div.attrs['class']:
                article_tag = div
            elif re.compile('content').pattern in div.attrs['class']:
                article_tag = div
            elif 'post-page' in div.attrs['class']:
                article_tag = div
            elif 'single-post' in div.attrs['class']:
                article_tag = div
            elif re.compile('container').pattern in div.attrs['class']:
                article_tag = div
            elif re.compile('entry').pattern in div.attrs['class']:
                article_tag = div

если тег div с атрибутами {'id': 'content'} - то скрипт срабатывает, но если тег с классом list div {'class': ['article']} - то не работает. подскажите, пожалуйста, как это можно пофиксить. вроде бы только это осталось поправить, остальное работает норм.
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
если тег div с атрибутами {'id': 'content'} - то скрипт срабатывает, но если тег с классом list div {'class': ['article']} - то не работает.
В этой строке
Python:
if re.compile('article') in div.attrs['class']:
нужно поменять re.compile('article') на re.compile('article').pattern
Python:
if re.compile('article').pattern in div.attrs['class']:
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
В этой строке
Python:
if re.compile('article') in div.attrs['class']:
нужно поменять re.compile('article') на re.compile('article').pattern
Python:
if re.compile('article').pattern in div.attrs['class']:

прошу прощения, это я при копипасте недосмотрел, к сожалению так тоже не работает
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
прошу прощения, это я при копипасте недосмотрел, к сожалению так тоже не работает
Проверил на этой ссылке sympaty.net/20160613/chto-delat-posle-solyariya/ - работает (тег находит).
Покажите на какой ссылке не работает.
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
Python:
tag1 = soup.find('article')
tag2 = soup.find('main', {'class': True})
tag3 = soup.find('div', itemtype='http://schema.org/Article')

div_list = soup.find_all('div')

if tag1:
    if len(tag1.find_all('p')) > 4:
        article_tag = tag1
elif tag2:
    if len(tag2.find_all('p')) > 4:
        article_tag = tag2
elif tag3:
    if len(tag3.find_all('p')) > 5:
        article_tag = tag3

else:
    for div in div_list:
        if len(div.find_all('p')) > 5:
            print(div.name, div.attrs)
            print('-' * 10)

            if div.has_attr('id'):
                if re.compile('content').pattern in div.attrs['id']:
                    article_tag = div
                elif re.compile('article').pattern in div.attrs['id']:
                    article_tag = div

            elif div.has_attr('class'):
                # print(type(div.attrs['class']))
                if re.compile('article').pattern in div.attrs['class']:
                    article_tag = div

                elif re.compile('main').pattern in div.attrs['class']:
                    article_tag = div

                elif re.compile('content').pattern in div.attrs['class']:
                    article_tag = div
                elif 'post-page' in div.attrs['class']:
                    article_tag = div
                elif 'single-post' in div.attrs['class']:
                    article_tag = div
                elif re.compile('container').pattern in div.attrs['class']:
                    article_tag = div
                elif re.compile('entry').pattern in div.attrs['class']:
                    article_tag = div


bipbap.ru/krasivye-kartinki/kartinki-krasivye-s-dnem-rozhdeniya-sveta-35-foto.html
может не совсем удачный урл, но в принципе все нужности есть
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
спасибо, буду разбираться) вообще я сам пытался сделать что-то типа re.match(tag, string), или re.findall, но не одолел превращения div.attrs['class'] в строку.
 

robisho

Активный пользователь
Пользователь
Окт 19, 2020
151
26
28
еще хотелось бы добавить фильтр, чтобы отсекать некоторые теги div
Python:
else:
    for div in div_list:
        if len(div.find_all('p')) > 5:
            if not re.search('footer', ' '.join(div.attrs.get('class', []))):
                print(div.name, div.attrs)
                print('-' * 10)
                ......

так работает, но есть еще пару атрибутов класса, например, comment, которые тоже нужно удалить. как это можно осуществить, кроме перебора через or?
 

Форум IT Специалистов