Веб-скребок, который выглядит для предварительно определенного слова в статьях новостей


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

Цель скрипта следующим образом:

  1. Парсить RSS-лента статей новости, опубликованные британским новости выход
  2. Читать в каждой новости статьи, перечисленные в ленте, заходя на link для каждого элемента в RSS-ленту.
  3. Поиск каждой статьи для предварительно определенных слов и фраз
  4. Записи результатов (соответствие кодовое слово/фраза и ссылка на статью) в CSV.

Вот код:

import feedparser
from bs4 import BeautifulSoup
from lxml import html
import requests
import re

import csv

words = ['divorce', 'custody battle', 'meal ticket', 'behind closed doors', 'detail of the case emerged on a legal database', ]

hit_article = []
links_list = []
hits = []
hit_link = []

d = feedparser.parse('http://www.dailymail.co.uk/articles.rss')

for item in d.entries:
    link = ( item[ "link" ] )
    links_list.append(link)
    title = ( item ["title"])


for link in links_list[:100]:
    page = requests.get(link)
    tree = html.fromstring(page.content)

    soup = BeautifulSoup(page.text, 'html.parser')

    text = soup.find('body')
    text = text.text

    for word in words:
        regex = r"\b"+ re.escape(word) + r"\b"
        match = re.search(regex, text)
        if match:
            print (word, ' ', title, "________found")
            hits.append(word)
            hit_link.append(str(link))

match_dictionary = dict(zip(hit_link, hits))

print (match_dictionary)

w = csv.writer(open("output.csv", "w"))
for key, val in match_dictionary.items():
    w.writerow([key, val])

Код делает то, что я хочу это делать, но я знаю, что это воняет (особенно там, где я падаю обратно на две for петли.

Как я могу достичь того же результата более аккуратно и Pythonically?



Комментарии
1 ответ

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


regex = r"\b"+ re.escape(word) + r"\b"

так как вы позаботился, чтобы избежать слова, и убедитесь, что он начинается и заканчивается на границах слова. (Вы можете сделать без учета регистра, хотя поиски? Кроме того, вы, возможно, захотите, чтобы обработать все пробелы в качестве эквивалента, в случае возникновения новой строки в середину фразы.)

У вас есть ошибка, однако, в том, что на экране отчеты выводить все делает как последний заголовок в RSS-ленту.

Организации код

Эта программа начинает достаточно долго, что вы должны разбить ее на функции. В частности, когда код становится длинным, все переменные (hit_article, links_list, hits, hit_link, dи т. д.) выступать в качестве глобальных переменных, что делает его трудно отслеживать, как они используются. Что является первопричиной вашей title ошибка.

Напрасный труд

Глупейшая ошибка в том, что tree = html.fromstring(page.content) никогда не используется, так что вы использовали lxml библиотека для парсинга html а второй раз без причины.

Менее очевидно, что у вас есть проблема, если одна статья содержит несколько условий поиска. На экране распечатке отчета все условия, которые были найдены. Однако, когда вы делаете dict(zip(hit_link, hits))вы только хранить последние нажмите на ссылку. Вам необходимо решить, хотите ли вы:


  • доклад все поисковые термины, которые встречаются в каждой статье (в этом случае необходимо изменить структуру данных для хранения больших результатов, или ликвидировать словарь целиком)

  • доклад только первый термин для поиска в списке, который появляется в каждой статье (в этом случае вы можете break от if match: блок)

  • доклад только первое вхождение в статье о любой из поисковых терминов (в этом случае вы должны построить регулярное выражение, чтобы искать любой из поисковых терминов: regex = r'\b(?:' + '|'.join(re.escape(word) for word in words) + ')\b'вместо зацикливания)

Списочные

В общем, когда вы видите картину:


output_list = []
for item in input_list:
output_list.append(transform(item))

... вы можете писать более изящно, используя список понимания.

Например, вместо:


list_list = []
d = feedparser.parse('http://www.dailymail.co.uk/articles.rss')
for item in d.entries:
link = ( item[ "link" ] )
links_list.append(link)

Вы должны написать (немного переименовать для ясности):

 rss_url = …
links = [entry['link'] for entry in feedparser.parse(rss_url).entries]

Предлагаемое решение

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

from bs4 import BeautifulSoup
import csv
import feedparser
import re
import requests

def search_article(url, phrases):
"""
Yield all of the specified phrases that occur in the HTML body of the URL.
"""
response = requests.get(url)
text = BeautifulSoup(response.text, 'html.parser').find('body').text
for phrase in phrases:
if re.search(r'\b' + re.escape(phrase) + r'\b', text):
yield phrase

def search_rss(rss_entries, phrases):
"""
Search articles listed in the RSS entries for phases, yielding
(url, article_title, phrase) tuples.
"""
for entry in rss_entries:
for hit_phrase in search_article(entry['link'], phrases):
yield entry['link'], entry['title'], hit_phrase

def main(rss_url, phrases, output_csv_path, rss_limit=None):
rss_entries = feedparser.parse(rss_url).entries[:rss_limit]
with open(output_csv_path, 'w') as f:
w = csv.writer(f)
for url, title, phrase in search_rss(rss_entries, phrases):
print('"{0}" found in "{1}"'.format(phrase, title))
w.writerow([url, phrase])

if __name__ == '__main__':
rss_url = 'http://www.dailymail.co.uk/articles.rss'
phrases = ['divorce', 'custody battle', …]
main(rss_url, phrases, 'output.csv', 100)

5
ответ дан 16 февраля 2018 в 11:02 Источник Поделиться