Обходное решение try-кроме применения ко многим операторам в одной строке, создав отдельную функцию - PullRequest
1 голос
/ 06 июля 2019

Я собираю данные словаря с сайта https://www.dictionary.com/.Цель состоит в том, чтобы удалить ненужные элементы со страниц словаря и сохранить их в автономном режиме для дальнейшей обработки.Из-за того, что веб-страницы несколько неструктурированы, могут присутствовать или не присутствовать элементы, упомянутые в приведенном ниже коде для удаления;отсутствие элементов дает исключение (во фрагменте 2).А поскольку в реальном коде есть много элементов, которые нужно удалить, и они могут присутствовать или отсутствовать, если мы применим try - except к каждому такому утверждению, строки кода будут резко увеличиваться.

Таким образом, яработая над решением этой проблемы, создав отдельную функцию для try - except (в фрагменте 3), идею которой я получил из здесь .Но я не могу получить код в фрагменте 3, работающий, так как команда, такая как soup.find_all('style'), возвращает None, где, как и должно быть, возвращен список всех тегов style, подобных фрагменту 2. Я не могу применить указанное решениенепосредственно, как иногда, мне нужно добраться до намеченного элемента, чтобы косвенно удалить его, ссылаясь на его parent или sibling, например, в soup.find('h2',{'class':'css-1iltn77 e17deyx90'}).parent

Фрагмент 1 используется для установки среды для выполнения кода.

Было бы замечательно, если бы вы могли предложить какое-нибудь предложение для работы с фрагментом 3.

Фрагмент 1 (Настройка среды для выполнения кода):

import urllib.request
import requests
from bs4 import BeautifulSoup
import re

headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
           'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',}

folder = "dictionary_com"

Фрагмент 2 (рабочий):

def makedefinition(url):
    success = False
    while success==False:
        try:
            request=urllib.request.Request(url,headers=headers)
            final_url = urllib.request.urlopen(request, timeout=5).geturl()
            r = requests.get(final_url, headers=headers, timeout=5)
            success=True
        except:
            success=False

    soup = BeautifulSoup(r.text, 'lxml')

    soup = soup.find("section",{'class':'css-1f2po4u e1hj943x0'})

    # there are many more elements to remove. mentioned only 2 for shortness
    remove = soup.find_all("style") # style tags
    remove.extend(safe_execute(soup.find('h2',{'class':'css-1iltn77 e17deyx90'}).parent)) # related content in the page

    for x in remove: x.decompose()

    return(soup)

# testing code on multiple urls
#url = "https://www.dictionary.com/browse/a"
#url = "https://www.dictionary.com/browse/a--christmas--carol"
#url = "https://www.dictionary.com/brdivowse/affection"
#url = "https://www.dictionary.com/browse/hot"
#url = "https://www.dictionary.com/browse/move--on"
url = "https://www.dictionary.com/browse/cuckold"
#url = "https://www.dictionary.com/browse/fear"
maggi = makedefinition(url)

with open(folder+"/demo.html", "w") as file:
    file.write(str(maggi))

Фрагмент 3 (не рабочий):

soup = None

def safe_execute(command):
    global soup
    try:
        print(soup) # correct soup is printed
        print(exec(command)) # this should print the list of style tags but printing None, and for related content this should throw some exception
        return exec(command) # None is being returned for style
    except Exception:
        print(Exception.with_traceback())
        return []

def makedefinition(url):
    global soup
    success = False
    while success==False:
        try:
            request=urllib.request.Request(url,headers=headers)
            final_url = urllib.request.urlopen(request, timeout=5).geturl()
            r = requests.get(final_url, headers=headers, timeout=5)
            success=True
        except:
            success=False

    soup = BeautifulSoup(r.text, 'lxml')

    soup = soup.find("section",{'class':'css-1f2po4u e1hj943x0'})

    # there are many more elements to remove. mentioned only 2 for shortness
    remove = safe_execute("soup.find_all('style')") # style tags
    remove.extend(safe_execute("soup.find('h2',{'class':'css-1iltn77 e17deyx90'}).parent")) # related content in the page

    for x in remove: x.decompose()

    return(soup)

# testing code on multiple urls
#url = "https://www.dictionary.com/browse/a"
#url = "https://www.dictionary.com/browse/a--christmas--carol"
#url = "https://www.dictionary.com/brdivowse/affection"
#url = "https://www.dictionary.com/browse/hot"
#url = "https://www.dictionary.com/browse/move--on"
url = "https://www.dictionary.com/browse/cuckold"
#url = "https://www.dictionary.com/browse/fear"
maggi = makedefinition(url)

with open(folder+"/demo.html", "w") as file:
    file.write(str(maggi))

1 Ответ

2 голосов
/ 07 июля 2019

В своем коде во фрагменте 3 вы используете встроенный метод exec, который возвращает None независимо от того, что он делает со своим аргументом. Подробнее см. этот SO поток.

Помощь

Используйте exec, чтобы изменить переменную и вернуть ее вместо возврата результата exec.

def safe_execute(command):
   d = {}
   try:
       exec(command, d)
       return d['output']
   except Exception:
       print(Exception.with_traceback())
       return []

Тогда назовите это как-то так:

remove = safe_execute("output = soup.find_all('style')")

EDIT:

После выполнения этого кода снова возвращается None. Однако при отладке внутри секции try, если мы print(soup) выводим правильное значение soup, но exec(command,d) дает NameError: name 'soup' is not defined.

Это несоответствие было преодолено с помощью eval() вместо exec(). Определенная функция:

def safe_execute(command):
    global soup
    try:
        output = eval(command)
        return(output)
    except Exception:
        return []

И звонок выглядит так:

remove = safe_execute("soup.find_all('style')")
remove.extend(safe_execute("soup.find('h2',{'class':'css-1iltn77 e17deyx90'}).parent"))
...