Многоуровневая проверка существования тегов в веб-очистке - улучшение читабельности в Python - PullRequest
0 голосов
/ 05 мая 2018

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

Страницы структурированы следующим образом:

<div id="content">
  <h1>Product name</h1>
  <table id="properties">
    <tbody>
      <tr id="manufacturer-row">
        <th>Manufacturer</th>
        <td>Some-Mark</td>
      </tr>
    </tbody>
  </table>
  <p>Full description of the product</p>
</div>

Условия, применимые к этому делу:

  1. Теги вложены так, что мне нужно проверить наличие каждого уровня,
  2. На некоторых страницах будут отсутствовать некоторые данные - пустой столбец в таблице такой же возможный, как и отсутствующая таблица,
  3. На некоторых страницах вообще не будет содержимого,
  4. Пустой текст в теге является допустимым значением, но пропущенный тег должен быть зарегистрирован,
  5. Отсутствующие данные не являются исключительной ситуацией.

По сути, я проверяю проверку наличия каждого фрагмента информации, что приводит к довольно трудно читаемому коду:

content = soup.select_one("#content")
if content:
    product_name_tag = content.select_one("h1")
    if product_name_tag:
        name = product_name_tag.text
    else:
        log("Product name tag not found")

    table = content.select_one("table")
    if table:
        manufacturer_tag = table.select_one("#manufacturer-row > td")
        if manufacturer_tag:
            manufacturer = manufacturer_tag.text
        else:
            log("Manufacturer tag not found")
    else:
        log("Table not found")
else:
    log("Tag '#content' not found")

return (
    name if name in locals() else None,
    manufacturer if manufacturer in locals() else None
)

В реальных приложениях код сложнее читать, так как свойства, которые я ищу, часто бывают более вложенными, и мне нужно проверить существование каждого тега перед извлечением его текста. Мне было интересно, есть ли какой-нибудь аккуратный способ справиться с этой проблемой с точки зрения читабельности кода и краткости? Мои идеи:

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

Создание оболочки для записи пропущенных фрагментов, если None был возвращен, а не под кодом «else» - для улучшения читаемости.

Помещение извлечения каждого фрагмента данных в отдельную функцию, как _get_content_if_available, _get_name_if_available

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

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

1 Ответ

0 голосов
/ 23 июля 2018

Все зависит от того, как вы хотите структурировать свой код. Мое предложение использовать ChainMap от collections. С ChainMap вы можете указать значения по умолчанию для ваших тегов / ключей и просто проанализировать значения, которые не пропущены. Таким образом, вы не будете иметь, если / еще загроможден вдоль вашей кодовой базы:

data = """<div id="content">
  <h1>Product name</h1>
  <table id="properties">
    <tbody>
      <tr id="manufacturer-row">
        <th>Manufacturer</th>
        <td>Some-Mark</td>
      </tr>
    </tbody>
  </table>
  <p>Full description of the product</p>
</div>"""

from bs4 import BeautifulSoup
from collections import ChainMap

def my_parse(soup):
    def is_value_missing(k, v):
        if v is None:
            print(f'Value "{k}" is missing!') # or log it!
        return v is None

    d = {}
    d['product_name_tag'] = soup.select_one("h1")
    d['manufacturer_tag'] = soup.select_one("#manufacturer-row td")
    d['description'] = soup.select_one("p")
    d['other value'] = soup.select_one("nav")   # this is missing!
    return {k:v.text for k, v in d.items() if is_value_missing(k, v) == False}

soup = BeautifulSoup(data, 'lxml')
c = ChainMap(my_parse(soup), {'product_name_tag': '-default name tag-',
             'manufacturer_tag': '-default manufacturer tag-',
             'description': '-default description-',
             'other value': '-default other value-',
             })

print("Product name = ", c['product_name_tag'])
print("Other value = ", c['other value'])

Будет напечатано:

Value "other value" is missing!
Product name =  Product name
Other value =  -default other value-
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...