Je souhaite trouver mes articles sur le forum de littérature obsolète e-bane.net . Certains modules du forum sont désactivés et je ne peux pas obtenir la liste des articles de leur auteur. De plus, le site n'est pas indexé par les moteurs de recherche comme Google, Yndex, etc.
La seule façon de trouver tous mes articles est d'ouvrir la page d'archive du site (fig.1). Ensuite, je dois sélectionner certains mois et années - par exemple janvier 201 (fig.1). Et puis je dois inspecter chaque article (fig.2) si au début est écrit mon pseudo - pa4080 (fig.3). Mais il y a quelques milliers d'articles.
J'ai lu peu de sujets comme suit, mais aucune des solutions ne correspond à mes besoins:
Je vais poster ma propre solution . Mais pour moi, c’est intéressant: Y at-il un moyen plus élégant de résoudre cette tâche?
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 __== '__main__':
main()
requirement.txt
:
aiohttp>=2.3.7
beautifulsoup4>=4.6.0
click>=6.7
Voici la version python3 du script (testée sur python3.5 sur Ubuntu 17.10 ).
Comment utiliser:
script.py
et le fichier de package est requirement.txt
.pip install -r requirement.txt
.python3 script.py pa4080
Il utilise plusieurs bibliothèques:
Ce qu'il faut savoir pour développer davantage le programme (autre que la documentation du paquet requis):
Comment ça marche:
Une idée pour le développer
Ce n'est pas la réponse la plus élégante, mais je pense que c'est mieux que d'utiliser la réponse bash.
Pour résoudre cette tâche, j'ai créé le prochain simple script bash qui utilise principalement l'outil CLI wget
name__.
#!/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\)$' | 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
Le script a trois fonctions:
La première fonction get_url_map()
utilise wget
en tant que --spider
(ce qui signifie qu'elle vérifiera simplement que des pages sont présentes) et créera le code récursif -r
, $MAP_FILE
du $TARGET_URL
avec le niveau de profondeur -l2
. (Un autre exemple pourrait être trouvé ici: Convertir un site Web en PDF ). Dans le cas présent, le $MAP_FILE
contient environ 20 000 URL.
La deuxième fonction filter_url_map()
simplifiera le contenu du $MAP_FILE
. Dans ce cas, nous n'avons besoin que des lignes (URL) contenant la chaîne article&sid
et elles sont environ 3000. Plus d'idées peuvent être trouvées ici: Comment supprimer des mots particuliers des lignes d'un fichier texte?
La troisième fonction get_key_urls()
utilisera wget -qO-
(en tant que commande curl
- examples ) pour générer le contenu de chaque URL à partir du $MAP_FILE
et tentera de trouver l’un quelconque des $KEY_WORDS
qu’il contient. Si l'un des $KEY_WORDS
est trouvé dans le contenu d'une URL particulière, cette URL sera enregistrée dans le $OUT_FILE
.
Au cours du processus de travail, la sortie du script ressemble à celle affichée sur l'image suivante. Il faut environ 63 minutes pour terminer s’il ya deux mots clés et 42 minutes lorsqu’un seul mot clé est recherché.
J'ai recréé mon script à partir de cette réponse fournie par @ karel . Maintenant, le script utilise lynx
au lieu de wget
. En conséquence, cela devient beaucoup plus rapide.
La version actuelle fait le même travail pendant 15 minutes quand il y a deux mots-clés recherchés et seulement 8 minutes si nous recherchons un seul mot-clé. C'est plus rapide que la solution Python fournie par @ dan .
De plus, lynx
offre une meilleure gestion des caractères non latins.
#!/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