I use scrapy and xpath to try to find a specific node in the DOM and try to render that portion as a string. This is what this portion looks like:
<p><strong>Description :</strong> SCP-3976 est l'appellation collective d'une série de manifestations
sous la forme de meurtres apparents de la célèbre autrice de romans policiers Agatha Christie<sup
class="footnoteref"><a id="footnoteref-1" href="javascript:;" class="footnoteref"
onclick="WIKIDOT.page.utils.scrollToReference('footnote-1')">1</a></sup>. À ce jour, toutes les instances
de SCP-3976 ont été localisées dans les zones rurales du territoire anglais et tous les composants de
chaque instances se trouvaient dans un rayon de 50 mètres. De plus, toutes les instances sont
apparues entre 22h31 et 08h36, heure locale, bien qu'aucun enregistrement ni témoin de la manifestation
d'une instance n'existent.</p>
with the following code
response.xpath("string(//p)").get()
I get that
Description : SCP-3976 est l'appellation collective d'une série de manifestations sous la forme de
meurtres apparents de la célèbre autrice de romans policiers Agatha Christie1. À ce jour, toutes les
instances de SCP-3976 ont été localisées dans les zones rurales du territoire anglais et tous les
composants de chaque instances se trouvaient dans un rayon de 50\xa0mètres. De plus, toutes les instances
sont apparues entre 22h31 et 08h36, heure locale, bien qu'aucun enregistrement ni témoin de la
manifestation d'une instance n'existent.
Which is a pretty good result. Only, you can see that next to Agatha Christie is the 1 of the <sup>element, I'd like via xpath to prevent it from appearing. Can I do that?
thanks,
Try:
response.xpath('//p/text()').extract()
You many need to strip the output from new lines, etc. to get it into the right shape, but it will exclude the 1.
You could make use of a couple of w3lib.html functions (remove_tags, remove_tags_with_content), to exclude specific tags from your results.
See example here.
Related
i am actually trying to parse a Wikipedia page for a project. I don't know how to automate my program in order to make him get the elements according to page order. This is the page : https://fr.wikipedia.org/wiki/Manga.
I want to get the text of h2 tags and p tags in the same order as they appear on Wikipedia. It's to put them in a database in the future in the right order in a table (lines on the same lvl : 1 title --> its paragraphs)
I tried the loop and looked over the doc of BeautifulSoup but can't find the solution.
This is my actual code :
import numbers
import urllib.request
from bs4 import BeautifulSoup
quote_page ='https://fr.wikipedia.org/wiki/Manga#:~:text=Un%20manga%20(%E6%BC%AB%E7%94%BB)%20est%20une,quelle%20que%20soit%20son%20origine.'
page = urllib.request.urlopen(quote_page)
soup = BeautifulSoup(page, 'html.parser')
for element in soup.select('h2 ~ p'):
for e in soup.select('h2 > span.mw-headline'):
print(e.text)
print(element.text)
#not working for the loops
Cause question / expected output is not that clear, this just should point into one possible direction.
You could select your captions, iterate over its .next_siblings and break iteration if there is a specific tag:
import urllib.request
from bs4 import BeautifulSoup
quote_page ='https://fr.wikipedia.org/wiki/Manga#:~:text=Un%20manga%20(%E6%BC%AB%E7%94%BB)%20est%20une,quelle%20que%20soit%20son%20origine.'
page = urllib.request.urlopen(quote_page)
soup = BeautifulSoup(page, 'html.parser')
for e in soup.select('h2 span.mw-headline'):
print(e.text)
for t in e.parent.next_siblings:
if t.name in ['h2','h4']:
break
if t.name == 'p':
print(t.text)
Output:
Étymologie
Le mot japonais « manga » souvent traduit littéralement par « image dérisoire » ou « dessin non abouti », est composé de « ga » (画), qui désigne la représentation graphique (« dessin », « peinture » ou toute image dessinée — comme l'estampe), et « man » (漫), « involontaire », « divertissant », « sans but », mais aussi « exagérer », « déborder » (qui peut être interprété comme caricature), ainsi qu'« au fil de l'idée ». Ainsi on pourrait aussi bien traduire ce mot par « dessin au trait libre », « esquisse au gré de la fantaisie », « image malhabile » ou tout simplement caricature ou grotesque dans le sens de Léonard de Vinci.
Le terme devient courant à partir de la fin du XVIIIe siècle avec la publication d'ouvrages tels que Mankaku zuihitsu (1771) de Kankei Suzuki, Shiji no yukikai (1798) de Kyōden Santō ou Manga hyakujo (1814) de Minwa Aikawa. Également en 1814, Hokusai, futur graveur de La Grande Vague de Kanagawa, donne à ses recueils d'estampes parfois grotesques le titre Hokusai manga. C'est ce dernier ouvrage qui fait connaître le mot en Occident. Il aurait été ainsi choisi pour son analogie avec un terme similaire dans l'ancien temps mais dont l'écriture diffère, et qui décrit la conservation de proies dans les becs des pélicans[7] indiquant des scènes prises sur le vif — comme l'oiseau fondant sur sa proie.
Il ne prend le sens précis de « bande dessinée » qu'au cours du XXe siècle, avec l'introduction de celle-ci au Japon. Lorsqu'elle y devient très populaire, après 1945 et grâce à Osamu Tezuka, le terme s'impose pour finir par ne plus désigner qu'elle. C'est ce terme qui a été utilisé à l'étranger (France, États-Unis, Allemagne, etc.), pour caractériser la bande dessinée japonaise, dont il est devenu un synonyme, et parfois grossièrement ramené à un genre.
Genre et nombre du mot « manga » en français
Le mot « manga » est pleinement intégré dans la langue française, comme l'atteste son intégration dans les dictionnaires usuels. Ceux-ci le donnent comme masculin (les mots japonais, eux, n'ont pas de genre grammatical), et c'est le genre qui prédomine largement. Toutefois, la première utilisation du mot en français revient à Edmond de Goncourt en 1895, dans une étude artistique dédiée à Hokusai[8], où il accorde « manga » au féminin pour désigner ce qu'il appela La Mangwa (sic) de l'artiste. Le terme revêtait alors plutôt le sens de « miscellanées », c'est-à-dire un recueil de nature disparate[9]. Depuis cette époque, manga a souvent été employé au féminin, et ce jusqu'à la popularisation de l'usage au masculin dans les années 1990 (notamment par les premiers journaux spécialisés et la télévision)[9]. Mais un argument en faveur de la féminisation du terme pourrait être que la locution équivalente en français, bande dessinée, est déjà de genre féminin. Plus récemment, l'auteur Frédéric Boilet parle de manga au féminin, notamment dans le cadre de son mouvement franco-japonais La Nouvelle Manga[10]
...
Hi i am trying to extract a particular line from the text extract of a pdf file but unable to do it
Below is my code
from tika import parser
parsed_pdf = parser.from_file("mypdf.pdf")
data =parsed_pdf['content']
print(data)
clean_data =data.strip()
regex = r':\s*\n+'
subst = ": "
for line in re.sub(regex, subst, clean_data, 0, re.MULTILINE).split('\n'):
if line != '':
print (line.strip())
The output what i am getting is
Bulletin des Annonces Légales Obligatoires – Annonce n° 2102361 du 31 mai 2021
31 mai 2021 BULLETIN DES ANNONCES LEGALES OBLIGATOIRES Bulletin n°65
2102361 Page 1
Avis de convocation / avis de réunion
FONCIERE 7 INVESTISSEMENT
Société Anonyme au capital de 1 120 000 euros
Siège social : 55, rue Pierre Charron – 75008 Paris
486 820 152 RCS PARIS
________________________________________
Rectificatif à l’avis de réunion paru le 26 mai 2021, bulletin n°63
Dans l’avertissement COVID, le troisième paragraphe est rectifié comme suit : « A l’occasion de cette Assemblée Générale Mixte du mardi 29 juin 2021 à 10 heures, au siège social,
il ne sera pas possible d’y assister personnellement. Dans ces conditions, les actionnaires sont invités
à voter par correspondance à l’aide du formulaire de vote ou à donner pouvoir, de préférence, au
Président ou sans indication de mandataire. »
Dans l’ordre du jour, à titre extraordinaire, la mention de la délibération, sur la « Délégation de
pouvoirs à donner au Conseil d’administration à l’effet de procéder à l’augmentation du capital
social par émission d’actions réservées aux adhérents d’un plan d’épargne d’entreprise établis en
application des articles L.225-129-6 du Code de commerce et L.3332-18 et suivants du Code » n’a
pas lieu d’être, aucune résolution n’étant présentée par le Conseil en ce sens.
Dans A - les conditions de Participation à l’Assemblée , 1. Formalités préalables à effectuer pour
participer à l’Assemblée générale, le texte est rectifié comme suit : (…)
Conformément à l’article R.225-85 du Code de commerce, pourront participer à l’Assemblée les
actionnaires qui justifieront : - s’il s’agit d’actions nominatives : d’un enregistrement comptable desdites actions dans les
comptes titres nominatifs de la Société au plus tard le deuxième jour ouvré précédant
l’Assemblée à zéro heure, heure de Paris, soit le vendredi 25 juin 2021 à zéro heure, heure de
Paris ;
- s’il s’agit d'actions au porteur : d’un enregistrement comptable desdites actions (le cas échéant
au nom de l’intermédiaire inscrit pour le compte de l’actionnaire concerné dans les conditions
légales et réglementaires) dans les comptes-titres au porteur tenus par leur intermédiaire
habilité au plus tard le deuxième jour ouvré précédant l’Assemblée à zéro heure, heure de Paris,
soit le vendredi 25 juin 2021 à zéro heure, heure de Paris. Les intermédiaires habilités
délivreront une attestation de participation, en annexe au formulaire de vote par
correspondance ou de procuration établis au nom de l’actionnaire ou pour le compte de
l’actionnaire représenté par l’intermédiaire inscrit.
Seuls les actionnaires justifiant de cette qualité au plus tard le deuxième jour ouvré précédant
l’Assemblée à zéro heure, heure de Paris, soit au le vendredi 25 juin 2021 à zéro heure, heure de
Paris, dans les conditions rappelées ci-dessus, pourront participer à cette Assemblée.
L’actionnaire pourra à tout moment céder tout ou partie de ses actions : - si le dénouement de la cession intervenait avant le vendredi 25 juin 2021 à zéro heure, heure de
Paris, le vote exprimé par correspondance, le pouvoir, éventuellement accompagnés d’une
attestation de participation, seraient invalidés ou modifiés en conséquence, selon le cas. A cette
fin, l'intermédiaire habilité teneur de compte devra notifier la cession à la Société et lui
transmettre les informations nécessaires ;
31 mai 2021 BULLETIN DES ANNONCES LEGALES OBLIGATOIRES Bulletin n°65
2102361 Page 2
- si le dénouement de la cession ou toute autre opération était réalisée après le vendredi 25 juin
2021, à zéro heure, heure de Paris, quel que soit le moyen utilisé, elle ne serait pas notifiée par
l'intermédiaire habilité ou prise en considération par la société Foncière 7 Investissement .
Dans B. Dépôt de questions écrites, le texte est rectifié comme suit.
Conformément à l’article R.225-84 du Code de commerce, l’actionnaire qui souhaite poser des
questions écrites peut, jusqu’au quatrième jour ouvré précédant la date de l’Assemblée au plus tard,
soit le mercredi 23 juin 2021, à minuit, heure de Paris, adresser ses questions par lettre
recommandée avec accusé de réception, à la société Foncière 7 Investissement 55, rue Pierre
Charron 75008 Paris, ou à l’adresse électronique contact#fonciere7investissement.fr. Pour être
prises en compte, elles doivent être accompagnées d’une attestation d’inscription, soit dans les
comptes au nominatif tenus par la Société, soit dans les comptes de titres au porteur tenus par un
intermédiaire mentionné à l’article L.211-3 du Code monétaire et financier.
Un avis de convocation sera publié au Bulletin des annonces légales obligatoires 15 jours au moins
avant la date de l’Assemblée générale.
Le présent Avis rectificatif est publié sur le site internet de la société : http://www.fonciere7investissement.fr
Le Conseil d'administration de la Société Foncière 7 Investissement
31 mai 2021 BULLETIN DES ANNONCES LEGALES OBLIGATOIRES Bulletin n°65
2102361 Page 3
How can extract only the 5th line from here which containsFONCIERE 7 INVESTISSEMENT
I have tried
print(line[5])
but getting output as
6
Any help is appreciated
How about print(clean_data.split('\n')[4])?
First, in python, list indices are zero-based, which means that when you ask for the index 5, you get the sixth element.
Second, line contains a single line! It isn't a collection of all lines, so line[5] will give you the sixth character in that line, not the fifth line overall.
So you should first get the list of all lines:
all_lines = re.sub(regex, subst, clean_data, 0, re.MULTILINE).split('\n')
Now, the fifth line in this list will be:
all_lines[4]
However, you might want to include your filter to remove blank lines so that all_lines doesn't contain any blank lines. You can do this with a regular loop, or a list comprehension:
all_lines = []
for line in re.sub(regex, subst, clean_data, 0, re.MULTILINE).split('\n'):
if line:
all_lines.append(line)
print(all_lines[4])
Or as a list-comp:
all_lines = [line for line in re.sub(regex, subst, clean_data, 0, re.MULTILINE).split('\n') if line]
We can skip the != "" in if line != "", because empty strings are Falsey.
However, since you're looking for a known term in the line, you can also filter by that:
search_term = "FONCIERE 7 INVESTISSEMENT"
all_lines_containing_term = [line for line in re.sub(regex, subst, clean_data, 0, re.MULTILINE).split('\n') if search_term in line.upper()]
Have you tried enumerate()?
for index, line in enumerate(re.sub(regex, subst, clean_data, 0, re.MULTILINE).split('\n')):
line = line.strip()
if not line:
continue
if index == 4:
print(line)
I have text which look like that
{"#context":"http://schema.org","#type":"Recipe","name":"Tartelette aux fraises et rhubarb'curd","recipeCategory":"tarte aux fraises","image":"https://assets.afcdn.com/recipe/20160527/8530_w1024h768c1cx1941cy2911.jpg","datePublished":"2012-10-10T08:48:00+02:00","prepTime":"PT90M","cookTime":"PT15M","totalTime":"PT105M","recipeYield":"4 personnes","recipeIngredient":["250 g de fraises","150 g de farine","45 g de beurre","40 g de sucre","1 oeuf","1 pinc\u00e9e de sel","20 cl de rhubarbe (r\u00e9cup\u00e9r\u00e9 de ma conception de compote)","3 oeufs","75 g de sucre","1 cuill\u00e8re \u00e0 soupe de ma\u00efzena"],"recipeInstructions":[{"#type":"HowToStep","text":"Pour 4 tartelettes ( ou une grande tarte mais je pr\u00e9f\u00e8re les tartelettes, la p\u00e2te sabl\u00e9e \u00e9tant difficile \u00e0 couper :)) "},{"#type":"HowToStep","text":"Pr\u00e9parer la p\u00e2te sabl\u00e9e : "},{"#type":"HowToStep","text":"Couper le beurre en petits morceaux."},{"#type":"HowToStep","text":"Mettre tous les ingr\u00e9dients dans un saladier et tout m\u00e9langer \u00e0 la main ( ou au robot) jusqu'\u00e0 former une boule homog\u00e8ne. La r\u00e9server au frais au moins 1h pour pouvoir mieux l'\u00e9taler."},{"#type":"HowToStep","text":"Pendant que la p\u00e2te sabl\u00e9es pose, pr\u00e9parer le rhubarb'curd : "},{"#type":"HowToStep","text":"M\u00e9langer le sucre avec le jus de rhubarbe sur feu doux, jusqu\u2019\u00e0 ce que le sucre soit fondu. "},{"#type":"HowToStep","text":"A part, dans un bol (qui pourra aller au bain marie dans l'\u00e9tape suivante), battre la ma\u00efzena avec 1 oeuf. Lorsqu\u2019elle est bien dissoute, incorporer les 2 autres oeufs, toujours en fouettant."},{"#type":"HowToStep","text":"Incorporer ensuite le jus de rhubarbe chaud en fouettant bien, le m\u00e9lange commence \u00e0 \u00e9paissir. Placer le bol au bain marie et faire \u00e9paissir sur feu doux tout en fouettant tr\u00e8s r\u00e9guli\u00e8rement."},{"#type":"HowToStep","text":"Une fois qu\u2019elle est bien \u00e9paisse, transf\u00e9rer dans un autre bol ou saladier pour la refroidir. "},{"#type":"HowToStep","text":"Pendant que le curd refroidit, cuire la p\u00e2te sabl\u00e9e \u00e0 blanc. Etaler la p\u00e2te sabl\u00e9e et la r\u00e9partir dans les 4 moules \u00e0 tartelette (ou dans un grand moule \u00e0 tarte). Puis enfourner entre 10 et 15 min (en fonction de votre four) \u00e0 200\u00b0C (thermostat 6-7)."},{"#type":"HowToStep","text":"Laisser refroidir les fonds une bonne demi heure."},{"#type":"HowToStep","text":"Monter les tartelettes : "},{"#type":"HowToStep","text":"- mettre une couche de rhubarb' curd dans les fonds de tarte"},{"#type":"HowToStep","text":"- laver et \u00e9queuter les fraises"},{"#type":"HowToStep","text":"- les couper en 2 et les disposer sur le rhubarb'curd."},{"#type":"HowToStep","text":"- conserver au frais avant de servir"}],
In the field RecipeInstructions I need to get everything write after "text": . I never used regular expression and I'm a little bit lost.
This looks like a json object but doesn't have [] around it to make it an actual list. You should be able to convert it into a Python native list of dictionaries and navigate it:
import json
recipe = json.loads('[' + your_text + ']')
steps = [obj["text"] for obj in recipe if obj.get("#type") == "HowToStep"]
What worries me though is that unless you truncated your text to post here, this may not be a well formed json. In which case you cannot use the above code and instead use regular expressions like this:
import re
regex = r"\"text\":\"([^\"]*)\""
matches = re.findall(regex, your_text)
'matches' should now be a list of all the text elements.
Curious about how this regex works? Here's a simulator
I am a beginner in Python and totally new to web scraping.
At the moment, I am trying to scrape this web page for educational purpose : https://www.cdiscount.com/maison/achat-meuble-literie/dormipur-matelas-140x190-memoire-de-forme-16-c/f-11755-v920t140190.html#rating
What i exactly want is to retrieve every comments of the multiple pages. As the URL doesn't change when i click on "précédent/suivant" (which means previous/next in French), I didn't know how to do.
See the image on the link:
I found this explanation to resolve the same problem : How to scrape multiple pages with an unchanging URL - Python 3
I followed the steps given by #Keyur Potdar but i can't retrieve, for example, the text of the second page with the following code. It only gives me the text of the first page :
import requests
from bs4 import BeautifulSoup
desc = {'productId': 'v920t140190',
'siteMapNodeId': 389,
'CurrentPage': 2,
'StarValueList':'',
'ReviewOrdering': 2,
}
r = requests.get('https://www.cdiscount.com/maison/achat-meuble-literie/dormipur-matelas-140x190-memoire-de-forme-16-c/f-11755-v920t140190.html#rating',params=desc)
soup = BeautifulSoup(r.text, 'html.parser')
tabinfos = []
for a in soup.find_all('p'):
tabinfos.append(a)
for i in range(0,len(tabinfos)):
print('Text :')
print(tabinfos[i].text)
The page comment data is populated via an ajax request. You can emulate the same using requests and get the data. You can use the network tab in the inspect tool to find out the ajax requests being made.
The data is you get is html itself so you don't have to do any additional work. You can use the same script you have been using in a loop but with the new ajax url.
import requests
from bs4 import BeautifulSoup
no_of_pages=5
all_comments=[]
for page in range(1,no_of_pages+1):
desc = {'productId': 'v920t140190',
'siteMapNodeId': 389,
'CurrentPage': page,
'StarValueList':'',
'ReviewOrdering': 2,
}
r = requests.get('https://www.cdiscount.com/RWDPSCustomerReviews.mvc/UpdatePagination',params=desc)
soup = BeautifulSoup(r.text, 'html.parser')
all_comments.extend([p.text.strip() for p in soup.find_all('p')])
for comment in all_comments:
print(comment)
Output:
ras bon produit bon rapport qualite prix envoie rapide
Acheté pour lit de chambre d'amis. Excellent rapport qualité / prix. Perso, je le trouve un peu dur.
Très satisfaite du confort de ce matelas, j’en possède déjà un ce qui me pousse en commander un autre pour ma maman.
Article conforme à la description
Couchage assez ferme / les tailles sont celles annoncées
Après 1 mois d'utilisation bon matelas s'adapte bien. Il est vrai qu'il est très dur au début. Depuis que je l'ai acheté je ne me leve plus la nuit pour aller au toilette je fait des nuit complete YOUPI, Je le recommande
Matelas agréable au coucher et toucher
Tout parfait comme descriptif
Je passe demain excellente nuits
très confortable après plusieurs mois d'utilisation, prend sa forme en deux jours
Matelas pour ma fille elle est très heureuse car elle dort bien.
Merci
très bon rapport qualité/prix. je recommande aux petits revenus!
Livre rapidement
Le seul bémol l'odeur du plastique
Super. Très bon rapport qualité prix. A recommander.
Livré en mon absence, le jour convenu devant ma porte donc super à ce niveau . Odeur de neuf un peu désagréable mais pas non persistante, dans une pièce bien aérée elle s'en ira en moins de 2 jours. Un peur dur, neuf oblige, mais plutôt bien pour les dos fragiles.
Confortable, des soucis de dos et maintenant nous dormons parfaitement bien!
Je suis très satisfaite de mon achat c'est un très bon produit. Très bon rapport qualité prix. Je recommande
Livraison rapide et matelas très bien conditionné "sous vide". (matelat roulé, reprend vite sa forme).
Mon grand le trouve confortable, ferme mais juste ce qu'il faut.
Matelas très confort le seul problème que j' ai du mal avec c est si il as une odeur assez forte est ce que ça reste longtemps ou pas?sinon oui je le conseille
Juste un epaisseur de 2 cm qui est memoire forme sans compter la house qui est juste memoire de forme que d un des deux coter
Cela fait le deuxième que j'achète en deux mois dans le cadre de l'installation de mes enfants, ces matelas sont très bien, j'ai été très agréablement surpris. Ils ont aussi l'énorme avantage d'être avant leur ouverture très facilement transportables et rentrent parfaitement sur les sièges arrières d'une petite voiture.
Pour un matelas à mémoire de forme, je trouve qu’il est bien ferme mais me convient quand même. A une assez forte odeur au déballage mais qui part au bout d’une journée (laisser aérée). Rapport qualité/prix il est pas mal. A voir au fil du temps.
En pleine forme lors du réveil, et ce, pdt 2 semaines !
Je dors dessus depuis le mois de novembre et j'en suis très satisfaite il est confortable dur et moelleux en même temps ce qu'il faut pour le dos
dommage tres dure mais faut si habituer ,mais bonne qualité
Le matelas en lui même est bien. Le gros problème c'est l'odeur qu'il dégage . Je ne sais si cela provient du matelas ou de l'emballage mais c'est très désagréable. Malgré l'aération du matelas et de la chambre cela reste très prononcé.
le matelas et bien ferme, j'ai plus de mal au dos, je dort nickel dessus.
Conforme à ce que je m'attendais.Je recommande vivement.
An alternative option would be to use selenium to emulate clicking 'next'.
In order to validate a migration tool, I want to compare two texts, one of the source A the other from the target tool B. To populate B, I'm using a reste API JSON-Based. B applies some "transformations" on the text that i don't know exactly what.
Here is the snippet of my code that i'm using:
bkclient = Client()
for mapping in BkMapping.select(): #get all objects
entity = Elgg_Entities.get(Elgg_Entities.guid == mapping.elgg_id)
if entity.subtype == 5:#postopic
pass
else:
elgg_desc = unicodedata.normalize("NFKD", entity.entity.description)
bk_content = unicodedata.normalize("NFKD", bkclient.get_post(mapping.bkid).get("data").get('content'))
if resume(bk_content) == resume(elgg_desc):
pass
else:
print('bk content')
print(bk_content)
print("elgg content")
print(elgg_desc)
input('continue...')
As result:
#### bk content (B) ######
<p>Bonjour à tous,</p>
<p>Je souhaite récolter des informations sur les actions menées en faveur des Seniors au sein du Groupe.</p>
<p>Si vous menez ou avez mené des actions en faveur des seniors et de leur insertion, pourriez-vous m’en faire part. Si je peux avoir des retours d’ici demain matin, ce serait super ! Merci d’avance de vos retours.</p>
######### elgg content (A) #########
<p>Bonjour à tous,</p>
<p>Je souhaite récolter des informations sur les actions menées en faveur des Seniors au sein du Groupe.</p>
<p>Si vous menez ou avez mené des actions en faveur des seniors et de leur insertion, pourriez-vous m’en faire part. Si je peux avoir des retours d’ici demain matin, ce serait super ! Merci d’avance de vos retours.</p>
So, I looking for a tool to make a hash of those texts which deals with accents <=>html entities, spaces, etc.
Found out how to do, BeautifulSoup can decode html entities :
def remove_accents(input_str):
nkfd_form = unicodedata.normalize('NFKD', input_str)
only_ascii = nkfd_form.encode('ASCII', 'ignore')
return only_ascii
def normalize(text):
text = re.sub(r'\s+', '', str(BeautifulSoup(text)), flags = re.MULTILINE)
return str(remove_accents(text))
def compare_text(s1, s2):
s1 = normalize(s1.lower())
s2 = normalize(s2.lower())
l1 = len(s1)
l2 = len(s2)
if l1 == l2:
return s1 == s2
return False
t1="""
<p>Bonjour à tous,</p>
<p>Je souhaite récolter des informations sur les actions menées en faveur des Seniors au sein du Groupe.</p>
<p>Si vous menez ou avez mené des actions en faveur des seniors et de leur insertion, pourriez-vous m’en faire part. Si je peux avoir des retours d’ici demain matin, ce serait super ! Merci d’avance de vos retours.</p>
"""
t2="""
<p>Bonjour à tous,</p>
<p>Je souhaite récolter des informations sur les actions menées en faveur des Seniors au sein du Groupe.</p>
<p>Si vous menez ou avez mené des actions en faveur des seniors et de leur insertion, pourriez-vous m’en faire part. Si je peux avoir des retours d’ici demain matin, ce serait super ! Merci d’avance de vos retours.</p>
"""
print(compare_text(t1, t2))
True