urllib.urlretrieve encoding is not kept - python

I'm using python 3.4.
When I use urllib.request.urlretrieve(link, filename="file.html") on a utf-8 file, the resulting file.html is not properly encoded. How do I make sure the file is encoded using utf-8?
How to implement the .decode(utf-8) in this case?
EDIT
This is the original part of page:
« Écoute, mon peuple, je parle ; Moi, Dieu, je suis ton Dieu ! Je ne t'accuse pas pour tes sacrifices ; tes holocaustes sont toujours devant moi. « Je ne prendrai pas un seul taureau de ton domaine, pas un bélier de tes enclos. Tout le gibier des forêts m'appartient et le bétail des hauts pâturages. « Si j'ai faim, irai-je te le dire ? Le monde et sa richesse m'appartiennent. Vais-je manger la chair des taureaux et boire le sang des béliers ? « Qu'as-tu à réciter mes lois, à garder mon alliance à la bouche, toi qui n'aimes pas les reproches et rejettes loin de toi mes paroles ? »
And this is what I get in the saved file:
� �coute, mon peuple, je parle ;�Moi, Dieu, je suis ton Dieu !�Je ne t'accuse pas pour tes sacrifices ; tes holocaustes sont toujours devant moi.�� Je ne prendrai pas un seul taureau de ton domaine, pas un b�lier de tes enclos.�Tout le gibier des for�ts m'appartient et le b�tail des hauts p�turages. � Si j'ai faim, irai-je te le dire ? Le monde et sa richesse m'appartiennent.�Vais-je manger la chair des taureaux et boire le sang des b�liers ?�� Qu'as-tu � r�citer mes lois,�� garder mon alliance � la bouche,�toi qui n'aimes pas les reproches et rejettes loin de toi mes paroles ?��
I noticed that in certain parts of the page accented characters are not really utf-8 encoded but the browser shows it properly. For example instead of É there is É and when the file is downloaded this seems to cause problems.

You can unescape the HTML escape sequences in the file line by line using the method shown here.
import html.parser
h = html.parser.HTMLParser()
with urllib.request.urlopen(link) as fin, open(
"file.html", 'w', encoding='utf-8') as fout:
for line in fin:
fout.write(h.unescape(line.decode('utf-8')))

I advice to use it handle this for you: It convert the loaded document implecitly to utf-8
markup = "<h1>Sacr\xc3\xa9 bleu!</h1>"
soup = BeautifulSoup(markup)
soup.h1
# <h1>Sacré bleu!</h1>
soup.h1.string
# u'Sacr\xe9 bleu!'
BeautifulSoup documentation: here

Related

BeautifulSoup - How to select elements according to page order?

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]
...

Regex locate number followed by space followed by character

I tried to locate only number followed by space and a character after it.
Exemple : text = "3 R"
and want it to be like this :
Exemple : text = "3. R"
i've tried this code :
text= re.sub(r'([0-9])(?!.*\d)', r'\1. ', text)
Am getting closer but don't know what should i add to it.
Update
Text :
Évitez les conversations malsaines en utilisant les 3 R, à savoir
‘reformuler, recentrer et réorienter’. Créez un cadre confortable en
reformulant les phrases susceptibles de générer des émotions négatives. Vous
pouvez également reformuler des reproches tels que : « Cela m’ennuie que tu
passes autant de temps sur des projets de moindre importance qui ne mènent
nulle part » en disant plutôt « J’aimerais que tu consacres les efforts que
tu fournis dans ton travail à davantage de nouveaux projets plutôt qu’à
quelques projets peu importants... Je suis sûr que tu disposes maintenant de
suffisamment d’expérience pour gérer des projets inédits et ambitieux. »
my regex code :
text= re.sub(r'\s*(?!\.[’"])([.,?:])(?!(?<=\d.)\d)\s*', r'\1 ', text)
text= re.sub(r'\s*([-])\s*', r'\1', text)
text= re.sub(u"\u2013", " ", text)
text= re.sub(r'(\d)\s+(?=\d)', r'\1', text)
text= re.sub(r'(\d)\/+(?=\d)', r'\1 ', text)
text= re.sub(r'([0-9])\b(?!.*\d)',r'\1. ', text)
Output:
Évitez les conversations malsaines en utilisant les 3 R, à savoir
‘reformuler, recentrer et réorienter’. Créez un cadre confortable en
reformulant les phrases susceptibles de générer des émotions négatives. Vous
pouvez également reformuler des
reproches tels que: Cela m’ennuie que tu passes autant de temps sur des
projets de moindre importance qui ne mènent nulle part en disant plutôt
J’aimerais que tu consacres les efforts que tu fournis dans ton travail à
davantage de nouveaux projets plutôt qu’à quelques projets peu importants, Je
suis sûr que tu disposes maintenant de suffisamment d’expérience pour gérer
des projets inédits et ambitieux.
i've tried the codes suggested by you guys but not working idk why, text is a long string.
The problem could be due to using too much regex??
I'm using python3.9
snippet
Based on the constraints you defined (input/output) and the discussion we had you can use this snippet:
re.sub(r"(\d+)(?:\s+)(\w)", r"\1. \2", text)
This works for me:
re.sub('(\d)\s([a-zA-Z])', r'\1. \2', text)
It replaces the 3 R with 3. R. Also works with bigger numbers, like 31789 R, and lowercase 3 r.

Regular expression findall python

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

How to scrape specific pages with an unchanging URL with requests

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'.

Compare two texts

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

Categories