Skip to content
Extraits de code Groupes Projets

Comparer les révisions

Les modifications sont affichées comme si la révision source était fusionnée avec la révision cible. En savoir plus sur la comparaison des révisions.

Source

Sélectionner le projet cible
No results found

Cible

Sélectionner le projet cible
  • la-quadrature-du-net/Attrap
  • foggyfrog/Attrap
  • skhwiz/Attrap
  • precambrien/Attrap
  • ketsapiwiq/Attrap
  • Joseki/Attrap
  • kr1p/attrap-pref-12
  • kr1p/attrap-pref-46
  • kr1p/attrap-pi
  • Guinness/Attrap
  • astroidgritty/attrap-pref-84
  • davinov/Attrap
  • maettellite/attrap-pref-01
  • m242/Attrap
  • multi/Attrap
  • mverdeil/Attrap
  • olpo/Attrap
17 résultats
Afficher les modifications
Validations sur la source (18)
......@@ -6,3 +6,4 @@ data/
pyvenv.cfg
output_*.log
*.patch
CACHEDIR.TAG
......@@ -244,6 +244,11 @@ test_pref59:
PREF: "pref59"
extends: .default_pref
test_pref61:
variables:
PREF: "pref61"
extends: .default_pref
test_pref62:
variables:
PREF: "pref62"
......@@ -284,6 +289,11 @@ test_pref75:
PREF: "pref75"
extends: .default_pref
test_pref76:
variables:
PREF: "pref76"
extends: .default_pref
test_pref77:
variables:
PREF: "pref77"
......@@ -334,6 +344,11 @@ test_pref976:
PREF: "pref976"
extends: .default_pref
test_prefbretagne:
variables:
PREF: "prefbretagne"
extends: .default_pref
test_prefidf:
variables:
PREF: "prefidf"
......
......@@ -9,6 +9,7 @@ import string
import logging
import requests
import time
from types import SimpleNamespace
import datetime
import json
from urllib.parse import quote
......@@ -169,7 +170,6 @@ class Attrap:
self.mastodon_prefix = ''
self.mastodon_suffix = ''
self.safe_mode = False
self.timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzname()
self.update_user_agent(user_agent)
......@@ -233,7 +233,7 @@ class Attrap:
self.session.proxies.update(proxies)
self.tor_requests = 0
def get_sub_pages(self, page_content, element, host, recursive_until_pdf):
def get_sub_pages(self, page_content, element, host, recursive_until_pdf, selenium=False):
"""
Récupère, à partir d'un chemin CSS, les sous-pages d'une page.
......@@ -241,6 +241,7 @@ class Attrap:
element -- Le chemin CSS vers l'objet renvoyant vers la sous-page recherchée
host -- Le nom d'hôte du site
recursive_until_pdf -- Un booléen pour savoir s'il faut rechercher un fichier PDF dans le chemin CSS. Le cas échéant, relance la recherche sur la sous-page si le lien n'est pas un PDF.
selenium -- lance un navigateur avec Selenium pour contourner les protections anti-robots
"""
soup = BeautifulSoup(page_content, 'html.parser')
sub_pages = []
......@@ -257,7 +258,8 @@ class Attrap:
sub_page_content,
element,
host,
recursive_until_pdf
recursive_until_pdf,
selenium=selenium
):
sub_pages.append(sub_sub_page)
else:
......@@ -274,7 +276,7 @@ class Attrap:
sub_pages.append(sub_page)
return sub_pages
def get_sub_pages_with_pager(self, page, sub_page_element, pager_element, details_element, host):
def get_sub_pages_with_pager(self, page, sub_page_element, pager_element, details_element, host, selenium=False):
"""
Récupère, à partir d'un chemin CSS, les sous-pages d'une page contenant un pager.
......@@ -283,12 +285,13 @@ class Attrap:
pager_element -- Le chemin CSS vers le lien de page suivante du pager
details_element -- Le chemin CSS vers l'objet contenant les détails de la sous-page recherchée
host -- Le nom d'hôte du site
selenium -- lance un navigateur avec Selenium pour contourner les protections anti-robots
"""
pages = []
if isinstance(page, bytes):
page = page.decode('utf-8')
if page.startswith('https://') or page.startswith('http://'):
page_content = self.get_page(page, 'get').content
page_content = self.get_page(page, 'get', selenium=selenium).content
else:
page_content = page
......@@ -321,13 +324,14 @@ class Attrap:
sub_page_element,
pager_element,
details_element,
host
host,
selenium=selenium
):
pages.append(sub_page)
return pages
def get_raa_with_pager(self, pages_list, pager_element, host, filter_from_last_element_date=False):
def get_raa_with_pager(self, pages_list, pager_element, host, filter_from_last_element_date=False, selenium=False):
"""
Récupère et analyse les RAA d'une page contenant un pager.
......@@ -337,11 +341,12 @@ class Attrap:
filter_from_last_element_date -- (Optionnel) Si la date du dernier élément de la dernière page parsée
n'est pas dans la plage temporelle voulue, ne charge pas les pages suivantes. Par défaut à False. Ne doit
être activé que si l'ordre des éléments est chronologique.
selenium -- lance un navigateur avec Selenium pour contourner les protections anti-robots
"""
elements = []
# On parse chaque page passée en paramètre
for page in pages_list:
page_content = self.get_page(page, 'get').content
page_content = self.get_page(page, 'get', selenium=selenium).content
# Pour chaque page, on récupère les PDF
for raa in self.get_raa_elements(page_content):
......@@ -400,12 +405,17 @@ class Attrap:
webdriver_options.add_argument("--disable-dev-shm-usage")
webdriver_options.add_argument("--use_subprocess")
webdriver_options.add_argument("--disable-blink-features=AutomationControlled")
webdriver_options.add_experimental_option("excludeSwitches", ["enable-automation"])
webdriver_options.add_experimental_option('useAutomationExtension', False)
if not self.user_agent == "":
webdriver_options.add_argument(f"--user-agent={self.user_agent}")
webdriver_options.add_argument("--headless")
webdriver_options.add_argument("--window-size=1024,768")
if self.tor_enabled:
webdriver_options.add_argument(f'--proxy-server=socks5://127.0.0.1:9050')
webdriver_options.add_argument("--headless=new")
webdriver_options.add_argument("--start-maximized")
display = Display(visible=False, size=(1024, 768))
display.start()
......@@ -430,6 +440,8 @@ class Attrap:
logger.warning(f'TimeoutException: {exc}')
if remaining_retries > 0:
time.sleep(5)
if self.tor_enabled:
self.tor_get_new_id()
return self.get_session(url, wait_element, (remaining_retries - 1))
else:
raise TimeoutException(exc)
......@@ -456,13 +468,14 @@ class Attrap:
f.write(data + "\n")
f.close()
def get_page(self, url, method, data={}):
def get_page(self, url, method, data={}, selenium=False):
"""
Récupère le contenu HTML d'une page web
url -- L'URL de la page demandée
method -- 'post' ou 'get', selon le type de requête
data -- Un dictionnaire contenant les données à envoyer au site
selenium -- lance un navigateur avec Selenium pour contourner les protections anti-robots
"""
try:
logger.debug(f'Chargement de la page {url}')
......@@ -477,10 +490,15 @@ class Attrap:
self.last_http_request = int(time.mktime(datetime.datetime.today().timetuple()))
page = None
if method == 'get':
page = self.session.get(url, timeout=(10, 120))
if method == 'post':
page = self.session.post(url, data=data, timeout=(10, 120))
if selenium and method == 'get':
page_content = self.get_session(url, None, 6)
page = {'content': page_content, 'status_code': 200}
page = SimpleNamespace(**page)
else:
if method == 'get':
page = self.session.get(url, timeout=(10, 120))
if method == 'post':
page = self.session.post(url, data=data, timeout=(10, 120))
if page.status_code == 429:
logger.warning('Erreur 429 Too Many Requests reçue, temporisation...')
......@@ -626,6 +644,7 @@ class Attrap:
# Donc on vérifie à nouveau ici si la date correspond à ce qu'on veut analyser
if not raa.date:
os.remove(f'{self.data_dir}/raa/{raa.get_sha256()}.pdf')
os.remove(f'{self.data_dir}/raa/{raa.get_sha256()}.json')
logger.error(f'ERREUR: le RAA {raa.name} n\'a pas de date !')
sys.exit(1)
if raa.date >= Attrap.get_aware_datetime(self.not_before, timezone=self.timezone):
......@@ -635,6 +654,10 @@ class Attrap:
self.ocr(raa, True)
raa.extract_content(self.data_dir)
self.search_keywords(raa, keywords)
else:
# On supprime le fichier de metadonnées puisqu'on ne le parsera pas
os.remove(f'{self.data_dir}/raa/{raa.get_sha256()}.pdf')
os.remove(f'{self.data_dir}/raa/{raa.get_sha256()}.json')
except PdfStreamError as exc:
logger.warning(f'ATTENTION: le RAA à l\'adresse {raa.url} n\'est pas valide ! On l\'ignore...')
except EmptyFileError as exc:
......
from Attrap_prefdpt import Attrap_prefdpt
class Attrap_pref61(Attrap_prefdpt):
# Configuration de la préfecture
hostname = 'https://www.orne.gouv.fr'
raa_page = f'{hostname}/Publications/Recueil-des-Actes-Administratifs-RAA/Recueil-des-Actes-Administratifs-RAA'
full_name = 'Préfecture de l\'Orne'
short_code = 'pref61'
timezone = 'Europe/Paris'
# Configuration des widgets à analyser
Attrap_prefdpt.grey_card['regex']['year'] = 'Le Recueil des actes administratifs ([0-9]{4})'
Attrap_prefdpt.grey_card['regex']['month'] = '([A-Za-zéû]* [0-9]{4})'
Attrap_prefdpt.grey_card['add_year_to_months'] = True
Attrap_prefdpt.white_card['regex']['month'] = '([A-Za-zéû]* [0-9]{4})'
Attrap_prefdpt.white_card['add_year_to_months'] = True
......@@ -15,7 +15,7 @@ class Attrap_pref75(Attrap):
# Config
hostname = 'https://www.prefectures-regions.gouv.fr'
raa_page = f'{hostname}/ile-de-france/tags/view/Ile-de-France/Documents+et+publications/Recueil+des+actes+administratifs'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0'
user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0'
full_name = 'Préfecture de Paris'
short_code = 'pref75'
timezone = 'Europe/Paris'
......@@ -23,17 +23,19 @@ class Attrap_pref75(Attrap):
def __init__(self, data_dir):
super().__init__(data_dir, self.user_agent)
self.enable_tor(10)
self.set_sleep_time(10)
def get_raa(self, keywords):
year_pages_to_parse = []
# On détermine quelles pages d'année parser
page_content = self.get_page(self.raa_page, 'get').content
page_content = self.get_session(self.raa_page, 'main', 6)
year_pages = self.get_sub_pages(
page_content,
'article.news-list-item header h2.news-list-title a',
self.hostname,
False
False,
selenium=True
)
for year_page in year_pages:
year_date = Attrap.guess_date(year_page['name'].strip(), '(?:.*Paris.*)([0-9]{4})').replace(day=1, month=1)
......@@ -42,13 +44,14 @@ class Attrap_pref75(Attrap):
pages_to_parse = []
for year_page in year_pages_to_parse:
page_content = self.get_page(year_page, 'get').content
page_content = self.get_session(year_page, 'main', 6)
year = BeautifulSoup(page_content, 'html.parser').select('div.breadcrumb div.container p span.active')[0].get_text().split('-')[-1].strip()
month_pages = self.get_sub_pages(
page_content,
'div.sommaire-bloc div.sommaire-content ol li a',
self.hostname,
False
False,
selenium=True
)[::-1]
for month_page in month_pages:
month_date = Attrap.guess_date(f"{month_page['name']} {year}", "(.*)").replace(day=1)
......@@ -57,7 +60,7 @@ class Attrap_pref75(Attrap):
elements = []
for page in pages_to_parse[::-1]:
page_content = self.get_page(page, 'get').content
page_content = self.get_session(page, 'main', 6)
for element in self.get_raa_elements(page_content):
elements.append(element)
......
from Attrap_prefdpt import Attrap_prefdpt
class Attrap_pref76(Attrap_prefdpt):
# Configuration de la préfecture
hostname = 'https://www.seine-maritime.gouv.fr'
raa_page = f'{hostname}/Publications/Recueils-des-actes-administratifs-RAA'
full_name = 'Préfecture de la Seine-Maritime'
short_code = 'pref76'
timezone = 'Europe/Paris'
# Configuration des widgets à analyser
Attrap_prefdpt.grey_card['regex']['year'] = '([0-9]{4})'
Attrap_prefdpt.grey_card['regex']['month'] = '([A-Za-zéû]* [0-9]{4})'
Attrap_prefdpt.grey_card['follow_link_on_unrecognised_date'] = False
......@@ -19,7 +19,7 @@ class Attrap_pref81(Attrap):
def __init__(self, data_dir):
super().__init__(data_dir, self.user_agent)
self.set_sleep_time(30)
self.set_sleep_time(5)
def get_raa(self, keywords):
year_pages_to_parse = []
......
......@@ -19,7 +19,7 @@ class Attrap_pref976(Attrap):
def __init__(self, data_dir):
super().__init__(data_dir, self.user_agent)
self.set_sleep_time(30)
self.set_sleep_time(5)
def get_raa(self, keywords):
year_pages_to_parse = []
......
import datetime
import time
from bs4 import BeautifulSoup
from urllib.parse import unquote
from Attrap import Attrap
class Attrap_prefbretagne(Attrap):
# Config
hostname = 'https://www.prefectures-regions.gouv.fr'
raa_page = f'{hostname}/bretagne/Documents-publications/Recueils-des-actes-administratifs/Recueil-des-actes-administratifs'
user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0'
full_name = 'Préfecture de la région Bretagne'
short_code = 'prefbretagne'
timezone = 'Europe/Paris'
def __init__(self, data_dir):
super().__init__(data_dir, self.user_agent)
self.enable_tor(10)
self.set_sleep_time(10)
def get_raa(self, keywords):
# page_content = self.get_page(self.raa_page, 'get').content
page_content = self.get_session(self.raa_page, 'main', 6)
elements = self.get_raa_elements(page_content)
time.sleep(10)
self.parse_raa(elements, keywords)
self.mailer()
def get_raa_elements(self, page_content):
elements = []
# On charge le parser
soup = BeautifulSoup(page_content, 'html.parser')
# Pour chaque balise a, on regarde si c'est un PDF, et si oui on le parse
for a in soup.select('main div.container.main-container div.col-main article.article div.texte div a.link-download'):
if a.get('href') and a['href'].endswith('.pdf'):
if a['href'].startswith('/'):
url = f"{self.hostname}{a['href']}"
else:
url = a['href']
url = unquote(url)
name = a.find('span').get_text().strip()
# On devine la date du RAA à partir du nom de fichier
guessed = Attrap.guess_date(name, '((?:[0-9]{2}|[0-9]{1})(?:er){0,1}[ _](?:[a-zéû]{3,9})[ _](?:[0-9]{4}|[0-9]{2}))')
if (guessed == datetime.datetime(9999, 1, 1, 0, 0)):
date = None
else:
date = guessed
raa = Attrap.RAA(url, date, name, timezone=self.timezone)
elements.append(raa)
return elements
......@@ -282,7 +282,13 @@ class Attrap_prefdpt(Attrap):
url = unquote(url)
name = a.find('span').previous_sibling.replace('Télécharger ', '').strip()
date = datetime.datetime.strptime(a.find('span').get_text().split(' - ')[-1].strip(), '%d/%m/%Y')
if not name:
name = url.split('/')[-1].strip()
try:
date = datetime.datetime.strptime(a.find('span').get_text().split(' - ')[-1].strip(), '%d/%m/%Y')
except Exception:
date = None
raa = Attrap.RAA(url, date, name, timezone=self.timezone)
elements.append(raa)
......
......@@ -12,25 +12,27 @@ class Attrap_prefidf(Attrap):
# Config
hostname = 'https://www.prefectures-regions.gouv.fr'
raa_page = f'{hostname}/ile-de-france/tags/view/Ile-de-France/Documents+et+publications/Recueil+des+actes+administratifs'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0'
full_name = 'Préfecture d\'Île-de-France'
user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0'
full_name = 'Préfecture de la région Île-de-France'
short_code = 'prefidf'
timezone = 'Europe/Paris'
def __init__(self, data_dir):
super().__init__(data_dir, self.user_agent)
self.enable_tor(10)
self.set_sleep_time(10)
def get_raa(self, keywords):
year_pages_to_parse = []
# On détermine quelles pages d'année parser
page_content = self.get_page(self.raa_page, 'get').content
page_content = self.get_session(self.raa_page, 'main', 6)
year_pages = self.get_sub_pages(
page_content,
'article.news-list-item header h2.news-list-title a',
self.hostname,
False
False,
selenium=True
)
for year_page in year_pages:
year_date = Attrap.guess_date(year_page['name'].strip(), '(?:.*[ÎIiî]le-de-[Ff]rance.*)([0-9]{4})').replace(day=1, month=1)
......@@ -39,13 +41,14 @@ class Attrap_prefidf(Attrap):
pages_to_parse = []
for year_page in year_pages_to_parse:
page_content = self.get_page(year_page, 'get').content
page_content = self.get_page(year_page, 'get', selenium=True).content
year = BeautifulSoup(page_content, 'html.parser').select('div.breadcrumb div.container p span.active')[0].get_text().split('-')[-1].strip()
month_pages = self.get_sub_pages(
page_content,
'div.sommaire-bloc div.sommaire-content ol li a',
self.hostname,
False
False,
selenium=True
)[::-1]
for month_page in month_pages:
month_date = Attrap.guess_date(f"{month_page['name']} {year}", "(.*)").replace(day=1)
......@@ -54,7 +57,7 @@ class Attrap_prefidf(Attrap):
elements = []
for page in pages_to_parse:
page_content = self.get_page(page, 'get').content
page_content = self.get_session(page, 'main', 6)
for element in self.get_raa_elements(page_content):
elements.append(element)
......
......@@ -12,24 +12,29 @@ class Attrap_prefpaca(Attrap):
# Config
hostname = 'https://www.prefectures-regions.gouv.fr'
raa_page = f'{hostname}/provence-alpes-cote-dazur/Documents-publications'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0'
full_name = 'Préfecture de Provence-Alpes-Côte-d\'Azur'
user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0'
full_name = 'Préfecture de la région Provence-Alpes-Côte-d\'Azur'
short_code = 'prefpaca'
timezone = 'Europe/Paris'
def __init__(self, data_dir):
super().__init__(data_dir, self.user_agent)
self.enable_tor(10)
self.set_sleep_time(10)
def get_raa(self, keywords):
# On récupère une session avec Selenium
page_content = self.get_session(self.raa_page, 'main', 6)
# On récupère les pages d'années
year_pages = []
for year_page in self.get_sub_pages_with_pager(
self.raa_page,
page_content,
'article.news-list-item header h2.news-list-title a',
'article.article div.content-pagination ul.pagination li.next a',
None,
self.hostname
self.hostname,
selenium=True
):
year = Attrap.guess_date(year_page['name'].strip(), 'RAA ([0-9]{4})').year
if year < 9999 and year >= self.not_before.year:
......@@ -37,7 +42,7 @@ class Attrap_prefpaca(Attrap):
elements = []
for year_page in year_pages:
page_content = self.get_page(year_page, 'get').content
page_content = self.get_session(year_page, 'main', 6)
for element in self.get_raa_elements(page_content):
elements.append(element)
......
make: ppparis pref01 pref02 pref03 pref04 pref05 pref06 pref09 pref10 pref11 pref13 pref2a pref2b pref25 pref29 pref30 pref31 pref33 pref34 pref35 pref38 pref39 pref42 pref44 pref49 pref50 pref52 pref54 pref55 pref59 pref62 pref63 pref64 pref65 pref66 pref69 pref73 pref75 pref77 pref80 pref81 pref83 pref87 pref91 pref92 pref93 pref94 pref976 prefidf prefpaca
make: ppparis pref01 pref02 pref03 pref04 pref05 pref06 pref09 pref10 pref11 pref13 pref2a pref2b pref25 pref29 pref30 pref31 pref33 pref34 pref35 pref38 pref39 pref42 pref44 pref49 pref50 pref52 pref54 pref55 pref59 pref61 pref62 pref63 pref64 pref65 pref66 pref69 pref73 pref75 pref76 pref77 pref80 pref81 pref83 pref87 pref91 pref92 pref93 pref94 pref976 prefbretagne prefidf prefpaca
ppparis:
bin/python3 cli.py ppparis
pref01:
......@@ -59,6 +59,8 @@ pref55:
bin/python3 cli.py pref55
pref59:
bin/python3 cli.py pref59
pref61:
bin/python3 cli.py pref61
pref62:
bin/python3 cli.py pref62
pref63:
......@@ -75,6 +77,8 @@ pref73:
bin/python3 cli.py pref73
pref75:
bin/python3 cli.py pref75
pref76:
bin/python3 cli.py pref76
pref77:
bin/python3 cli.py pref77
pref80:
......@@ -95,6 +99,8 @@ pref94:
bin/python3 cli.py pref94
pref976:
bin/python3 cli.py pref976
prefbretagne:
bin/python3 cli.py prefbretagne
prefidf:
bin/python3 cli.py prefidf
prefpaca:
......
......@@ -86,6 +86,7 @@ Vous pouvez également activer le safe mode en spécifiant la variable d'environ
- Préfecture de Meurthe-et-Moselle (identifiant : `pref54`)
- Préfecture de la Meuse (identifiant : `pref55`)
- Préfecture du Nord (identifiant : `pref59`)
- Préfecture de l'Orne (identifiant : `pref61`)
- Préfecture du Pas-de-Calais (identifiant : `pref62`)
- Préfecture du Puy-de-Dôme (identifiant : `pref63`)
- Préfecture des Pyrénées-Atlantiques (identifiant : `pref64`)
......@@ -94,6 +95,7 @@ Vous pouvez également activer le safe mode en spécifiant la variable d'environ
- Préfecture du Rhône (identifiant : `pref69`)
- Préfecture de la Savoie (identifiant : `pref73`)
- Préfecture de Paris (identifiant : `pref75`)
- Préfecture de la Seine-Maritime (identifiant : `pref76`)
- Préfecture de Seine-et-Marne (identifiant : `pref77`)
- Préfecture de la Somme (identifiant : `pref80`)
- Préfecture du Tarn (identifiant : `pref81`)
......@@ -104,8 +106,9 @@ Vous pouvez également activer le safe mode en spécifiant la variable d'environ
- Préfecture de Seine-Saint-Denis (identifiant : `pref93`)
- Préfecture du Val-de-Marne (identifiant : `pref94`)
- Préfecture de Mayotte (identifiant : `pref976`)
- Préfecture d'Île-de-France (identifiant : `prefidf`)
- Préfecture de Provence-Alpes-Côte-d'Azur (identifiant : `prefpaca`)
- Préfecture de la région Bretagne (identifiant : `prefbretagne`)
- Préfecture de la région Île-de-France (identifiant : `prefidf`)
- Préfecture de la région Provence-Alpes-Côte-d'Azur (identifiant : `prefpaca`)
## Contributions
......
......@@ -74,6 +74,7 @@ available_administrations = [
'pref54',
'pref55',
'pref59',
'pref61',
'pref62',
'pref63',
'pref64',
......@@ -82,6 +83,7 @@ available_administrations = [
'pref69',
'pref73',
'pref75',
'pref76',
'pref77',
'pref80',
'pref81',
......@@ -92,6 +94,7 @@ available_administrations = [
'pref93',
'pref94',
'pref976',
'prefbretagne',
'prefidf',
'prefpaca'
]
......