Я хочу найти свои статьи на устаревшем (устаревшем) литературном форуме e-bane.net . Некоторые из модулей форума отключены, и я не могу получить список статей их авторов. Также сайт не индексируется поисковыми системами как Google, Yndex и т. Д.
Единственный способ найти все мои статьи - открыть страницу архива сайта (рис.1). Затем я должен выбрать определенный год и месяц - например, январь 2013 г. (рис. 1). И тогда я должен проверить каждую статью (рис.2), пишется ли в начале мой ник - pa4080 (рис.3). Но есть несколько тысяч статей.
Я прочитал несколько тем следующим образом, но ни одно из решений не соответствует моим потребностям:
Я опубликую мое собственное решение . Но для меня интересно: Есть ли более изящный способ решить эту задачу?
script.py
:
#!/usr/bin/python3
from urllib.parse import urljoin
import json
import bs4
import click
import aiohttp
import asyncio
import async_timeout
BASE_URL = 'http://e-bane.net'
async def fetch(session, url):
try:
with async_timeout.timeout(20):
async with session.get(url) as response:
return await response.text()
except asyncio.TimeoutError as e:
print('[{}]{}'.format('timeout error', url))
with async_timeout.timeout(20):
async with session.get(url) as response:
return await response.text()
async def get_result(user):
target_url = 'http://e-bane.net/modules.php?name=Stories_Archive'
res = []
async with aiohttp.ClientSession() as session:
html = await fetch(session, target_url)
html_soup = bs4.BeautifulSoup(html, 'html.parser')
date_module_links = parse_date_module_links(html_soup)
for dm_link in date_module_links:
html = await fetch(session, dm_link)
html_soup = bs4.BeautifulSoup(html, 'html.parser')
thread_links = parse_thread_links(html_soup)
print('[{}]{}'.format(len(thread_links), dm_link))
for t_link in thread_links:
thread_html = await fetch(session, t_link)
t_html_soup = bs4.BeautifulSoup(thread_html, 'html.parser')
if is_article_match(t_html_soup, user):
print('[v]{}'.format(t_link))
# to get main article, uncomment below code
# res.append(get_main_article(t_html_soup))
# code below is used to get thread link
res.append(t_link)
else:
print('[x]{}'.format(t_link))
return res
def parse_date_module_links(page):
a_tags = page.select('ul li a')
hrefs = a_tags = [x.get('href') for x in a_tags]
return [urljoin(BASE_URL, x) for x in hrefs]
def parse_thread_links(page):
a_tags = page.select('table table tr td > a')
hrefs = a_tags = [x.get('href') for x in a_tags]
# filter href with 'file=article'
valid_hrefs = [x for x in hrefs if 'file=article' in x]
return [urljoin(BASE_URL, x) for x in valid_hrefs]
def is_article_match(page, user):
main_article = get_main_article(page)
return main_article.text.startswith(user)
def get_main_article(page):
td_tags = page.select('table table td.row1')
td_tag = td_tags[4]
return td_tag
@click.command()
@click.argument('user')
@click.option('--output-filename', default='out.json', help='Output filename.')
def main(user, output_filename):
loop = asyncio.get_event_loop()
res = loop.run_until_complete(get_result(user))
# if you want to return main article, convert html soup into text
# text_res = [x.text for x in res]
# else just put res on text_res
text_res = res
with open(output_filename, 'w') as f:
json.dump(text_res, f)
if __name__ == '__main__':
main()
requirement.txt
:
aiohttp>=2.3.7
beautifulsoup4>=4.6.0
click>=6.7
Здесь приведена версия скрипта на python3 (протестирована на python3.5 в Ubuntu 17.10 ]).
Как использовать:
script.py
, а файл пакета - requirement.txt
. pip install -r requirement.txt
. python3 script.py pa4080
Он использует несколько библиотек:
Что нужно знать для дальнейшего развития программы (кроме документации по запросу пакет):
Как это работает:
Некоторая идея, чтобы ее можно было развивать дальше.
Это не самый элегантный ответ, но я думаю, что это лучше, чем использование ответа bash.
Я воссоздал свой сценарий на основе этого ответа , предоставленного @karel . Теперь сценарий использует lynx
вместо wget
. В результате это становится значительно быстрее.
Текущая версия выполняет ту же работу в течение 15 минут, когда есть два искомых ключевых слова, и только 8 минут , если мы ищем только одно ключевое слово. Это быстрее, чем решение Python , предоставленное @dan .
Кроме того, lynx
обеспечивает лучшую обработку нелатинских символов.
#!/bin/bash
TARGET_URL='http://e-bane.net/modules.php?name=Stories_Archive'
KEY_WORDS=('pa4080') # KEY_WORDS=('word' 'some short sentence')
MAP_FILE='url.map'
OUT_FILE='url.list'
get_url_map() {
# Use 'lynx' as spider and output the result into a file
lynx -dump "${TARGET_URL}" | awk '/http/{print $2}' | uniq -u > "$MAP_FILE"
while IFS= read -r target_url; do lynx -dump "${target_url}" | awk '/http/{print $2}' | uniq -u >> "${MAP_FILE}.full"; done < "$MAP_FILE"
mv "${MAP_FILE}.full" "$MAP_FILE"
}
filter_url_map() {
# Apply some filters to the $MAP_FILE and keep only the URLs, that contain 'article&sid'
uniq "$MAP_FILE" | grep -v '\.\(css\|js\|png\|gif\|jpg\|txt\) | grep 'article&sid' | sort -u > "${MAP_FILE}.uniq"
mv "${MAP_FILE}.uniq" "$MAP_FILE"
printf '\n# -----\nThe number of the pages to be scanned: %s\n' "$(cat "$MAP_FILE" | wc -l)"
}
get_key_urls() {
counter=1
# Do this for each line in the $MAP_FILE
while IFS= read -r URL; do
# For each $KEY_WORD in $KEY_WORDS
for KEY_WORD in "${KEY_WORDS[@]}"; do
# Check if the $KEY_WORD exists within the content of the page, if it is true echo the particular $URL into the $OUT_FILE
if [[ ! -z "$(lynx -dump -nolist "${URL}" | grep -io "${KEY_WORD}" | head -n1)" ]]; then
echo "${URL}" | tee -a "$OUT_FILE"
printf '%s\t%s\n' "${KEY_WORD}" "YES"
fi
done
printf 'Progress: %s\r' "$counter"; ((counter++))
done < "$MAP_FILE"
}
# Call the functions
get_url_map
filter_url_map
get_key_urls
Для решения этой задачи я создал следующий простой bash-скрипт, который в основном использует инструмент CLI wget
.
#!/bin/bash
TARGET_URL='http://e-bane.net/modules.php?name=Stories_Archive'
KEY_WORDS=('pa4080' 's0ther')
MAP_FILE='url.map'
OUT_FILE='url.list'
get_url_map() {
# Use 'wget' as spider and output the result into a file (and stdout)
wget --spider --force-html -r -l2 "${TARGET_URL}" 2>&1 | grep '^--' | awk '{ print $3 }' | tee -a "$MAP_FILE"
}
filter_url_map() {
# Apply some filters to the $MAP_FILE and keep only the URLs, that contain 'article&sid'
uniq "$MAP_FILE" | grep -v '\.\(css\|js\|png\|gif\|jpg\|txt\) Скрипт имеет три функции:
-
Первая функция get_url_map()
использует wget
как --spider
(что означает, что он просто проверяет наличие страниц). ) и создаст рекурсивный -r
URL $MAP_FILE
из $TARGET_URL
с уровнем глубины -l2
. (Другой пример можно найти здесь: Конвертировать сайт в PDF ). В текущем случае $MAP_FILE
содержит около 20 000 URL-адресов.
-
Вторая функция filter_url_map()
упростит содержание $MAP_FILE
. В этом случае нам нужны только строки (URL), которые содержат строку article&sid
, а их около 3000. Больше идей можно найти здесь: Как удалить определенные слова из строк текстового файла?
-
Третья функция get_key_urls()
будет использовать wget -qO-
(как команда curl
- examples ) для вывода содержимого каждого URL из $MAP_FILE
и постараюсь найти любой из $KEY_WORDS
внутри него. Если какой-либо из $KEY_WORDS
найден в содержимом какого-либо конкретного URL, этот URL будет сохранен в $OUT_FILE
.
В процессе работы вывод скрипта выглядит так, как показано на следующем изображении. Для завершения требуется около 63 минут, если есть два ключевых слова, и 42 минуты , когда выполняется поиск только по одному ключевому слову.
| grep 'article&sid' | sort -u > "${MAP_FILE}.uniq"
mv "${MAP_FILE}.uniq" "$MAP_FILE"
printf '\n# -----\nThe number of the pages to be scanned: %s\n' "$(cat "$MAP_FILE" | wc -l)"
}
get_key_urls() {
counter=1
# Do this for each line in the $MAP_FILE
while IFS= read -r URL; do
# For each $KEY_WORD in $KEY_WORDS
for KEY_WORD in "${KEY_WORDS[@]}"; do
# Check if the $KEY_WORD exists within the content of the page, if it is true echo the particular $URL into the $OUT_FILE
if [[ ! -z "$(wget -qO- "${URL}" | grep -io "${KEY_WORD}" | head -n1)" ]]; then
echo "${URL}" | tee -a "$OUT_FILE"
printf '%s\t%s\n' "${KEY_WORD}" "YES"
fi
done
printf 'Progress: %s\r' "$counter"; ((counter++))
done < "$MAP_FILE"
}
# Call the functions
get_url_map
filter_url_map
get_key_urls
Скрипт имеет три функции:
Первая функция get_url_map()
использует wget
как --spider
(что означает, что он просто проверяет наличие страниц). ) и создаст рекурсивный -r
URL $MAP_FILE
из $TARGET_URL
с уровнем глубины -l2
. (Другой пример можно найти здесь: Конвертировать сайт в PDF ). В текущем случае $MAP_FILE
содержит около 20 000 URL-адресов.
Вторая функция filter_url_map()
упростит содержание $MAP_FILE
. В этом случае нам нужны только строки (URL), которые содержат строку article&sid
, а их около 3000. Больше идей можно найти здесь: Как удалить определенные слова из строк текстового файла?
Третья функция get_key_urls()
будет использовать wget -qO-
(как команда curl
- examples ) для вывода содержимого каждого URL из $MAP_FILE
и постараюсь найти любой из $KEY_WORDS
внутри него. Если какой-либо из $KEY_WORDS
найден в содержимом какого-либо конкретного URL, этот URL будет сохранен в $OUT_FILE
.
В процессе работы вывод скрипта выглядит так, как показано на следующем изображении. Для завершения требуется около 63 минут, если есть два ключевых слова, и 42 минуты , когда выполняется поиск только по одному ключевому слову.