While loop to get information from HTML code in python - python

Trying to create a code that will get reviewer's name and reviews from Booking.com.
I was able to get all the necessary URLs and isolate reviewer's name and comments from the HTML code but I'm struggling to create a while to go to the next review.
The while loop should take the reviewer's name append it to the list, move to the next name append it and so forth. I also need to the same for the comment.
When running the code nothing happens and I'm not sure where my issue is.
#Loop parameters
##HTMLs
#Booking.com URL
search_url[0] = 'https://www.booking.com/reviews/us/hotel/shore-cliff.es.html?label=gen173nr-1DEgdyZXZpZXdzKIICOOgHSDNYBGiTAogBAZgBCrgBF8gBDNgBA-gBAYgCAagCA7gC5bPZkQbAAgHSAiQzMTc3NTA4OS00OGRkLTQ5ZjYtYjBhNi1kOWEzYzZhN2QwOWXYAgTgAgE;sid=3e3ae22b47e3df3ac2590eb19d37f888;customer_type=total;hp_nav=0;old_page=0;order=featuredreviews;page=1;r_lang=all;rows=75&'
link = search_urls[0] #Just the first one to try
url = link
html = urllib.request.urlopen(url).read().decode('utf-8') #loading each search page
#Main HTML of first hotel
index=html.find('class="review_list"')
review_list_html = html[index:]
##Lists:
hotels=[]
reviewer_name=[]
review_comment=[]
#Creating counter variable
counter=0
reviewercount =0
#Main HTML of first hotel
index=html.find('class="review_list"')
review_list_html = html[index:]
reviewer_html = review_list_html[review_list_html.find('reviewer_name'):]
review_html = review_list_html[review_list_html.find('class="review_pos ">'):]
#Loop to get reviewer
while review_list_html.find('reviewer_name'):
#Get reviewer's name
#Start of reviewers name
start =reviewer_html.find('<span itemprop="name">')+22 #To ignore <span itemprop="name"> and jump right the name
start
#End of reviewers name
end =reviewer_html.find('</span>')
#Isolating reviewers name
reviewer_html=reviewer_html[start:end]
#Adding reviewer to list
reviewer_name.append(reviewer_html)

Your issue is that every next index lookup you need to start from previous index, otherwise you will create eternal loop. Generally it's more common to use HTML parsers like Beautiful Soup, but it's absolutely possible to parse this page with method you're trying to use.
We can use "reviewer_name" as main index for every review block. Starting from this index we will get indexes of "name" and </span>. Text between those indexes is reviewer's name. To parse review body we will find all indexes of "reviewBody" before index of next review block.
Full code:
from urllib.request import urlopen
link = "https://www.booking.com/reviews/us/hotel/shore-cliff.es.html"
with urlopen(link) as request:
response = request.read().decode()
reviews = []
name_pos = response.find('"reviewer_name"') # find first review
while name_pos >= 0:
name = ""
review_blocks = []
start_pos = response.find('"name"', name_pos)
end_pos = response.find("</span>", start_pos)
if end_pos > start_pos >= 0:
name = response[start_pos + 7: end_pos]
prev_name_pos = name_pos
name_pos = response.find('"reviewer_name"', name_pos + 1) # get next review
start_pos = response.find('"reviewBody"', prev_name_pos, name_pos)
while start_pos >= 0:
end_pos = response.find("</span>", start_pos)
if end_pos > start_pos >= 0:
review_blocks.append(response[start_pos + 13: end_pos])
start_pos = response.find('"reviewBody"', start_pos + 1, name_pos)
reviews.append((name, "\n".join(review_blocks)))
reviews content:
[
('Adriana',
'Nada para criticar.\n'
'Impecable lugar, habitación con vistas hermosas cualquiera sea. Camas '
'confortables, pequeña cocina completa, todo impecable.\n'
'La atención en recepción excelente, no se pierdan las cookies que convidan '
'por la tarde allí. El desayuno variado y con unos tamales exquisitos! Cerca '
'de todo.'),
('Ana', 'Todo excelente'),
('Lara',
'simplemente un poco de ruido en el tercer piso pero solo fue un poco antes '
'de las 10:00pm\n'
'realmente todo estaba excelente, ese gran detalle de el desayuno se les '
'agradece mucho.'),
('Rodrigo',
'Todo me gustó solo lo único que me hubiera gustado que también tuvieran es '
'unas chimeneas.\n'
'El hotel tiene una hermosa vista y se puede caminar y disfrutar por toda la '
'orilla de la playa hasta llegar al muelle y mas lejos si uno quiere.'),
('May', 'Me encanto q estaba abierta la piscina 👍🌊el mar expectacular'),
('Scq', 'Las vistas al Pacífico'),
('Eva', 'Desayuno\nUbicación y limpieza'),
('Marta',
'Muy buena ubicación y vistas al mar. Habitaciones modernas, amplias y con '
'cocina. Buen desayuno y hasta las 10, a diferencia de otros hoteles en los '
'que estuvimos. Personal muy amable. El chek out es a las 12 por lo que te '
'permite disfrutar de las piscina y de las vistas y paseo por la costa.'),
('Filippo',
'Habitación enorme, y muy limpio. \n'
'La habitación con vista al Ocean .... top'),
('Enrique', 'La atención del personal'),
('Lucia',
'El lugar para el desayuno es demasiado pequeño y no hay lugar suficiente '
'para sentarse\n'
'La vista, los jardines y todo el entorno son preciosos. Y es muy '
'confortable!'),
('Pablo', 'El precio.\nLa ubicación y el desayuno'),
('Walter', 'El hotel está bien, la ubicación es buena'),
('Anónimo', 'Muy bueno, el personal muy amable\nExcelente lugar muy cómodo'),
('Gonzalo', ''),
('Maria', ''),
('Rosana', ''),
('Leticia', ''),
('María', ''),
('Samantha', '')
]

Related

Get full text below h2 tag with BeautifulSoup

I am trying to obtain different character's descriptions and habilities for a dataset.
The problem I've encountered is that there seems to be a span tag within the h2 tag and in some cases a figure before de p tags. This is the format I'm facing:
<h2><span class="mw-headline" id="Apariencia">Apariencia</span></h2>
<figure class="thumb tleft " style="width: 100px">
<p>...</p>
<p>...</p>
<p>...</p>
<h2><span class="mw-headline" id="Personalidad">Personalidad</span></h2>
<p>...</p>
<p>...</p>
<p>...</p>
I need to obtain the text in those paragraphs.
I have tried something like this but it obviously does not work.
import urllib.request
from bs4 import BeautifulSoup
fp = urllib.request.urlopen("https://jojo.fandom.com/es/wiki/Star_Platinum")
mybytes = fp.read()
html_doc = mybytes.decode("utf8")
fp.close()
soup = BeautifulSoup(html_doc, 'html.parser')
spans = soup.find_all('span', {"class": "mw-headline"})
for s in spans:
print(s.nextSibling.getText)
You can search backwards for previous <h2> and store result in the dictionary:
from bs4 import BeautifulSoup
html_doc = '''\
<h2><span class="mw-headline" id="Apariencia">Apariencia</span></h2>
<figure class="thumb tleft " style="width: 100px">
<p>T1 ...</p>
<p>T2 ...</p>
<p>T3 ...</p>
<h2><span class="mw-headline" id="Personalidad">Personalidad</span></h2>
<p>T4 ...</p>
<p>T5 ...</p>
<p>T6 ...</p>'''
soup = BeautifulSoup(html_doc, 'html.parser')
out = {}
for p in soup.select('p'):
previous_h2 = p.find_previous('h2')
out.setdefault(previous_h2.text, []).append(p.text)
print(out)
Prints:
{
'Apariencia': ['T1 ...', 'T2 ...', 'T3 ...'],
'Personalidad': ['T4 ...', 'T5 ...', 'T6 ...']
}
I think in this case, .select with CSS selectors would be very useful.
To get all the section headers, you can use the selector h2:has(span.mw-headline[id]); and to get a specific header by the id attribute value (hId below) of the span nested within, you can use the selector hSel below.
hId = 'Personalidad' # for example
hSel = f'h2:has(span.mw-headline[id="{hId}"])'
Then, to get all the tags after that header, you can use f'{hSel}~*', but you also need to filter out the tags after the next header to only get tags in that section, so the full selector would be
sSel = f'{hSel}~*:not({hSel}~h2~*):not(h2)'
[:not({hSel}~h2~*) filters out the tags after the next header, and :not(h2) filters out the next header tag itself.]
Making use of this, the function below returns a dictionary containing section header, text and html.
def get_wikiSection(header, wSoup, sec1Header='? Abstract ?'):
sSel = 'h2:has(span.mw-headline[id])'
sSel = f'*:has(~{sSel}):not({sSel}~*)'
if not header: hId = hSel = None # [first section has no header]
elif isinstance(header, str):
hId, hSel = header, f'h2:has(span.mw-headline[id="{header}"])'
header = wSoup.select_one(hSel)
if not header: return {'errorMsg': f'Not found: {hSel}'}
else: hId = header.select_one('span.mw-headline[id]')['id']
## header SHOULD BE: None/hId/a tag containing span.mw-headline[id] ##
if hId:
hSel = f'h2:has(span.mw-headline[id="{hId}"])'
sSel = f'{hSel}~*:not({hSel}~h2~*):not(h2)'
header = header.get_text(' ').strip()
else: header = sec1Header
sect = wSoup.select(sSel)
sText = '\n'.join([s.get_text(' ').strip() for s in sect])
sHtml = '\n'.join([''.join(s.prettify().splitlines()) for s in sect])
if not sect: sText = sHtml = None
return {'headerId': hId, 'sectionHeader': header,
'sectionText': sText, 'sectionHtml': sHtml}
For example, get_wikiSection('Personalidad', soup) will return
{
'headerId': 'Personalidad',
'sectionHeader': 'Personalidad',
'sectionText': 'Jotaro describió a Star Platinum como un ser muy violento. Suele ser silencioso, excepto cuando lanza golpes, gritando "ORA ORA ORA" en voz alta, bastante rápido y repetidamente. Con una cara relativamente humana ha demostrado tener expresiones tales como fruncir el ceño y sonreír.\nStar Platinum demuestra un tipo de interés en la auto-conservación, como se ve cuando detiene una bala que Jotaro dispara experimentalmente en su propia cabeza, protege a un Jotaro incapacitado de los ataques de DIO durante los efectos de su The World , y lo revive de cerca de la muerte directamente hacia latir su corazón (Sin embargo, considerando el papel pionero de Star Platinum en la serie, esta capacidad puede hablar principalmente de cualidades metafísicas o subconscientes genéricas para los usuarios de Stand).\nEn el manga original, Star Platinum desde el principio se ve con una sonrisa amplia y desconcertante. Más tarde, Star Platinum gana el rostro estoico de Jotaro, cualquier sonrisa futura va a advertir a la persona que se dirige a un gran dolor inminente.\nStar Platinum lleva el nombre de la carta del Tarot La Estrella , que simboliza el optimismo, el discernimiento y la esperanza.',
'sectionHtml': '<p> Jotaro describió a Star Platinum como un ser muy violento. Suele ser silencioso, excepto cuando lanza golpes, gritando "ORA ORA ORA" en voz alta, bastante rápido y repetidamente. Con una cara relativamente humana ha demostrado tener expresiones tales como fruncir el ceño y sonreír.</p>\n<p> Star Platinum demuestra un tipo de interés en la auto-conservación, como se ve cuando detiene una bala que Jotaro dispara experimentalmente en su propia cabeza, protege a un Jotaro incapacitado de los ataques de DIO durante los efectos de su The World , y lo revive de cerca de la muerte directamente hacia latir su corazón (Sin embargo, considerando el papel pionero de Star Platinum en la serie, esta capacidad puede hablar principalmente de cualidades metafísicas o subconscientes genéricas para los usuarios de Stand).</p>\n<p> En el manga original, Star Platinum desde el principio se ve con una sonrisa amplia y desconcertante. Más tarde, Star Platinum gana el rostro estoico de Jotaro, cualquier sonrisa futura va a advertir a la persona que se dirige a un gran dolor inminente.</p>\n<p> Star Platinum lleva el nombre de la carta del Tarot <a class="extiw" href="http://en.wikipedia.org/wiki/es:La_Estrella_(Tarot)" title="wikipedia:es:La Estrella (Tarot)"> La Estrella </a> , que simboliza el optimismo, el discernimiento y la esperanza.</p>'
}
If you want all the sections:
h2Tags = soup.select('h2:has(span.mw-headline[id])')
wikiSections = [get_wikiSection(h, soup) for h in [None]+h2Tags]
The resulting list of dictionaries can also be converted to a pandas DataFrame as simply as pd.DataFrame(wikiSections).

Get all text from all divs with the same class name in Python Selenium

I want to get all today's big games from Bet365 (Warning its a gambling site!) but i only can get one which is the first game.
I try this but no working:
try:
driver.maximize_window()
driver.get("https://www.bet365.es/#/HO/")
driver.implicitly_wait(5)
except:
print("No se ha podido acceder a Bet365. Comprueba si la página funciona correctamente y no estas bloqueado de ella.")
try:
partidos_destacados = driver.find_element_by_class_name("fh-ParticipantFixtureTeam_TruncateName")
for i in partidos_destacados:
print(i.text)
except:
print("No se han podido leer los partidos destacados. Comprueba si ha cambiado el nombre del class en el HTML de BET365")
There are many divs with class name "fh-ParticipantFixtureTeam_TruncateName" and I need get all of this.
I encountered the error. Im using driver.find_element_by_class_name but i need use driver.find_elements_by_class_name.
This is the code fixed:
try:
driver.maximize_window()
driver.get("https://www.bet365.es/#/HO/")
driver.implicitly_wait(5)
except:
print("No se ha podido acceder a Bet365. Comprueba si la página funciona correctamente y no estas bloqueado de ella.")
try:
partidos_destacados = driver.find_elements_by_class_name("fh-ParticipantFixtureTeam_TruncateName")
for i in partidos_destacados:
print(i.text)
except:
print("No se han podido leer los partidos destacados. Comprueba si ha cambiado el nombre del class en el HTML de BET365")

How do I merge items from a list avoiding repeated content inside the items?

Edit 4:
Simpler example of what I want to do:
I have a list like this:
sentences = ['Hello, how are','how are you','you doing?']
And I want to turn it into a string like this:
sentence = 'Hello, how are you doing?'
Any help is appreciated!
Original post:
I'm trying to get highlighted text out of a .pdf file and put it inside a .docx file with that same name.
Here's the code for it:
from typing import List, Tuple
import fitz # install with 'pip install pymupdf'
import os
from docx import Document
def _parse_highlight(annot: fitz.Annot, wordlist: List[Tuple[float, float, float, float, str, int, int, int]]) -> str:
points = annot.vertices
quad_count = int(len(points) / 4)
sentences = []
for i in range(quad_count):
# where the highlighted part is
r = fitz.Quad(points[i * 4: i * 4 + 4]).rect
words = [w for w in wordlist if fitz.Rect(w[:4]).intersects(r)]
sentences.append(" ".join(w[4] for w in words))
sentence = " ".join(sentences)
return sentence
def handle_page(page):
wordlist = page.getText("words") # list of words on page
wordlist.sort(key=lambda w: (w[3], w[0])) # ascending y, then x
separator = "PÁGINA NÚMERO " + str(page.number) + ": "
highlights = []
annot = page.firstAnnot
while annot:
if annot.type[0] == 8:
highlights.append(separator)
highlights.append(_parse_highlight(annot, wordlist))
document.add_paragraph(highlights)
annot = annot.next
return highlights
def main(filepath: str) -> List:
doc = fitz.open(filepath)
highlights = []
for page in doc:
highlights += handle_page(page)
return highlights
dir_files = [f for f in os.listdir(".") if os.path.isfile(os.path.join(".", f))]
print(dir_files)
document = Document()
for file in dir_files: # look at every file in the current directory
if file.endswith('.pdf'): # if it is a PDF, use it
print('Working on converting: ' + file)
main(file)
document.save(file.replace(".pdf",".docx"))
I have to say I didn't write the part for getting the text out of the highlights. I got it here.
The problem is the list it creates gets repeated items. Here's a sample of the output to the .docx file:
PÁGINA NÚMERO 0: En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, que desempena un papel importante en la organizacion a través del citoplasma (fig. 6-20). que desempena un papel importante en la organizacion estructuras y las actividades de la célula,
PÁGINA NÚMERO 0: En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, revelado la presencia del citoesqueleto, una red de
As you can see it repeats the same thing more than once.
I think it's because my pdf is split in two like this:
But it would be an inconvinience to split every page in half and create a longer pdf. Another problem could be that I OCR'd this pdf and maybe it's causing issues (this is why the text output is slightly different from the pdf but I'm fine with that).
So I'm looking for a way to check if the "highlights" list has repeated items and delete them. Or maybe check before they get added to the list and not add them. But I'm not that experienced in programming so I'm asking for your help!
Any help is appreciated!
And sorry for any bad english!
Edit 1:
I've now tried doing this:
...
def main(filepath: str) -> List:
doc = fitz.open(filepath)
highlights = []
for page in doc:
highlights += handle_page(page)
highlights = set(highlights)
highlights = list(highlights)
document.add_paragraph(highlights)
return highlights
...
But it doesn't work. It even changes the order of the items because it deletes stuff that was added first and I don't want that.
Edit 2:
I think I found what's giving me trouble.
I did print(sentences) before they get joined into "sentence" and this is what I get:
['En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte', 'primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados', 'pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia', 'en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras', 'microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto,', 'revelado la presencia del citoesqueleto, una red de fibras que se extiende a través del citoplasma (fig. 6-20). El citoesqueleto, que desempena un papel importante en la organizacion', 'a través del citoplasma (fig. 6-20). que desempena un papel importante en la organizacion estructuras y las actividades de la célula,']
As you can see the items inside "sentences" contain one another so even if I used set(sentences) it wouldn't work. The OCR had something to do with that I'm pretty sure.
So now I think I need to shift my focus into crossreferencing each item inside "sentences".
Like when doing A+B-(A∩B)=C. This means C wouldn't have duplicates and, if the order is correct, would make a comprihensible sentence. But I'm completely blank in if there's even a way to accomplish this.
I also learned this to eliminate dupes and still keep the order of a list: list(dict.fromkeys())
Edit 3:
Only ruunning this code:
def _parse_highlight(annot: fitz.Annot, wordlist: List[Tuple[float, float, float, float, str, int, int, int]]) -> str:
points = annot.vertices
quad_count = int(len(points) / 4)
sentences = []
for i in range(quad_count):
# where the highlighted part is
r = fitz.Quad(points[i * 4: i * 4 + 4]).rect
words = [w for w in wordlist if fitz.Rect(w[:4]).intersects(r)]
sentences.append(" ".join(w[4] for w in words))
print(sentences)
sentence = " ".join(sentences)
print(sentence)
return sentence
def handle_page(page):
wordlist = page.getText("words") # list of words on page
wordlist.sort(key=lambda w: (w[3], w[0]))
separator = "PÁGINA NÚMERO " + str(page.number) + ": "
highlights = []
annot = page.firstAnnot
while annot:
if annot.type[0] == 8:
highlights.append(separator)
highlights.append(_parse_highlight(annot, wordlist))
annot = annot.next
return highlights
def main(filepath: str) -> List:
doc = fitz.open(filepath)
highlights = []
for page in doc:
highlights += handle_page(page)
print(highlights)
return highlights
main(example.pdf)
This is the only thing I higlighted:
Here's what the terminal says:
['El citoesqueleto es una red de fibras que organiza las estructuras', 'citoesqueleto es una red de que organiza las estructuras y las actividades', 'las estructuras y las actividades de la célula', 'En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte', 'primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados', 'pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia', 'en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras', 'microscopia 6ptica como en la microscopia revelado la presencia del citoesqueleto, extiende a través del citoplasma']
El citoesqueleto es una red de fibras que organiza las estructuras citoesqueleto es una red de que organiza las estructuras y las actividades las estructuras y las actividades de la célula En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia revelado la presencia del citoesqueleto, extiende a través del citoplasma
['PÁGINA NÚMERO 0: ', 'El citoesqueleto es una red de fibras que organiza las estructuras citoesqueleto es una red de que organiza las estructuras y las actividades las estructuras y las actividades de la célula En los primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte primeros tiempos de la microscopia electronica los bi- logos pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados pensaban que los organulos de una célula eucarionte flota- ban libremente en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia en el citosol. Pero los progresos realizados tanto en la microscopia 6ptica como en la microscopia electronica han revelado la presencia del citoesqueleto, una red de fibras microscopia 6ptica como en la microscopia revelado la presencia del citoesqueleto, extiende a través del citoplasma']
Now I see that some text I didn't highlight is also being turned into a "sentences" item. Again, I believe this to be the OCR at fault or maybe I could tune the pymupdf better idk.
You can use python set in order to remove duplicates. It should be done AFTER the loop and BEFORE the return
highlights = ['h1', 'h2', 'h1']
no_dups_highlights = list(set(highlights))
print(no_dups_highlights)
output
['h2', 'h1']
So set shuffles the members; you can use it, but only as a helper.
There are several ways to do this:
Use a set to keep track of what you've already seen:
seen = set()
filtered = []
for highlight in highlights:
if highlight not in seen:
filtered.append(highlight)
seen.add(highlight)
If you're using a sufficiently recent version of Python, dict maintains order:
filtered = list({highlight: None for highlight in highlights}.keys())
Ignore the theoretical slowness and just code it in the most straightforward way; it'll likely be fast enough in practice:
filtered = []
for highlight in highlights:
if highlight not in filtered:
filtered.append(higwhlight)
All these are for exact matching; if you end up needing approximate matching, you'll probably have to use the last approach anyway, with some sort of similarity measure.
I've implemented another method of getting the 'sentences' list out of the pdf and now it works as intended.
This is the code:
from typing import List, Tuple
import fitz
import os
from docx import Document
_threshold_intersection = 0.9
def _check_contain(r_word, points):
r = fitz.Quad(points).rect
r.intersect(r_word)
if r.getArea() >= r_word.getArea() * _threshold_intersection:
contain = True
else:
contain = False
return contain
def _parse_highlight(annot: fitz.Annot, wordlist: List[Tuple[float, float, float, float, str, int, int, int]]) -> str:
quad_points = annot.vertices
quad_count = int(len(quad_points) / 4)
sentences = ['' for i in range(quad_count)]
for i in range(quad_count):
points = quad_points[i * 4: i * 4 + 4]
words = [
w for w in wordlist if
_check_contain(fitz.Rect(w[:4]), points)
]
sentences[i] = ' '.join(w[4] for w in words)
sentence = ' '.join(sentences)
document.add_paragraph(sentence)
return sentence
def handle_page(page):
wordlist = page.getText("words")
wordlist.sort(key=lambda w: (w[3], w[0]))
highlights = []
annot = page.firstAnnot
while annot:
if annot.type[0] == 8:
highlights.append(_parse_highlight(annot, wordlist))
annot = annot.next
return highlights
def main(filepath: str) -> List:
doc = fitz.open(filepath)
highlights = []
for page in doc:
document.add_paragraph("Page Number " + str(page.number+1) + ": ")
highlights += handle_page(page)
return highlights
dir_files = [f for f in os.listdir(".") if os.path.isfile(os.path.join(".", f))]
document = Document()
for file in dir_files:
if file.endswith('.pdf'):
print('Working on converting: ' + file)
main(file)
document.save(file.replace(".pdf",".docx"))
Got this new method here.

Problem with save in csv information of web scraping

I am working on retrieving information from pages through web scraping, my code does not throw errors, but I have problems wanting to save this information in a kind of database. I leave my code to see if anyone can help me, I create the csv file, but at the moment, it saves absolutely nothing:
import requests
from bs4 import BeautifulSoup
import csv
from urllib.request import urlopen
#MOTOR DE BUSQUEDA
#Rastreo de sitios a traves de la barra de búsqueda
class Content: #Pensamos las noticias como objetos o elementos, por ende creamos la clase Content para extraer el contenido del sitio "el ciudadano"
def __init__(self, topic, url, title, body, data): #se crea de esta forma una clase, donde tenemos el titulo, el cuerpo, la url y el topico de búsqueda.
self.topic = topic #leemos la palabra clave de búsqueda
self.title = title #leemos el titulo
self.body = body #leemos el cuerpo
self.url = url #leemos la URL
self.data = data #leemos la fecha de publicación
def print(self):
print("New article found for topic: {}".format(self.topic))
print("TITLE: {}".format(self.title))
print("BODY:\n{}".format(self.body))
print("URL: {}".format(self.url))
print("DATA: {}".format(self.data))
class Website: #Clase que guarda las propiedades del sitio
"""
Contiene informacion sobre la estructura del sitio web
"""
def __init__(self, name, url, searchUrl, resultListing, resultUrl, absoluteUrl, titleTag, bodyTag, dataTag):
self.name = name
self.url = url
self.searchUrl = searchUrl #contiene al boton de busqueda
self.resultListing = resultListing #elemento que ve en resultado todas las listas encontradas
self.resultUrl = resultUrl #define la etiqueta donde se encuentra el link que queremos acceder
self.absoluteUrl=absoluteUrl #creamos el valor boleano para ver si la url es absoluta o relativa,
#absoluta = True relativa=False
self.titleTag = titleTag #etiqueta del titulo de la noticia
self.bodyTag = bodyTag #etiqueta del cuerpo de la noticia
self.dataTag = dataTag #etiqueta de la fecha de publicacion de la noticia
class Crawler: #Clase que toma la URL y devuelve el objeto BeautifulSoup
#Aqui trata las excepciones y posibles errores
def getPage(self, url): #funcion que toma una url y devuelve un objeto BeautifulSoup
try:
req = requests.get(url)
except requests.exceptions.RequestException:
return None
return BeautifulSoup(req.text, 'html.parser')
#funcion de utilidad que nos encontrara los elementos dentro del BeautifulSoup
def safeGet(self, pageObj, selector):
childObj = pageObj.select(selector)
if childObj is not None and len(childObj) > 0:
#return childObj[0].get_text() solo entrega el primero
return '\n'.join(
[elem.get_text() for elem in childObj]) #entrega todos las busquedas
return "" #Si no pasa eso, retorna vacio o none.
def search(self, topic, site): #ingresamos un tópico y el sitio
"""
Busca en un sitio web determinado un tema determinado y registra todas las paginas
encontradas
"""
bs = self.getPage(site.searchUrl + topic) #recibe la URL del sitio con el tópico
searchResults = bs.select(site.resultListing) #definimos el objeto que contiene todos los resultados
registrocontenido = []
for result in searchResults: #como son varios, accedimos uno por uno
url = result.select(site.resultUrl)[0].attrs["href"] #el atributo href contiene los links
# Verifica si es una URL relativa o absoluta.
if(site.absoluteUrl):
bs = self.getPage(url) #si es absoluta
else:
bs = self.getPage(site.url + url) #si es relativa
if bs is None:
print("Tenemos un problema!!")
return
title = self.safeGet(bs, site.titleTag) #usamos la funcion de utilidad safeGet
body = self.safeGet(bs, site.bodyTag)
data = self.safeGet(bs, site.dataTag)
if title != '' and body != '':
#Si titulo y cuerpo son distintos de vacios, imprimos.
content = Content(topic, url, title, body, data)
content.print()
registrocontenido.append(content)
return registrocontenido
def writeArticles(filename, articles):
csvFile = open(filename,'wt+',encoding='utf-8')
writer=csv.writer(csvFile)
try:
for article in articles:
csvrow = [article,topic,article.title,article.data,article.body,article.url]
finally:
csvFile.close()
crawler = Crawler()
#siteData=[nombre, urlprincipal,url busqueda, etiquetaresultado,etiqueta título en lista de resultados,url absoluta,titulo de la noticia, cuerpo, fecha]
siteData = [['El ciudadano', 'https://www.elciudadano.com/', 'https://www.elciudadano.com/?s=', 'div.td_module_16 ','h3.entry-title a', True, 'h1.entry-title', 'div.td-post-content p', 'time']]
sites = []
for row in siteData:
sites.append(Website(row[0], row[1], row[2],row[3], row[4], row[5], row[6], row[7], row[8]))
topics = ['PYTHON'] #variable de topicos que queremos extraer
articles = []
for topic in topics:
print("GETTING INFO ABOUT: " + topic)
for targetSite in sites: #for para recorrer los sitios
articles.extend(crawler.search(topic, targetSite))
crawler.search(topic, targetSite) #llamamos a la funcion search
writeArticles('Articulos.csv', articles)
I appreciate any help or suggestion please!
In your method to save the content to csv, you are not writing to the file. the code just opens and closes the file pointer. Use writer.writerow()
def writeArticles(filename, articles):
csvFile = open(filename,'wt+',encoding='utf-8')
writer=csv.writer(csvFile)
try:
for article in articles:
writer.writerow([article,topic,article.title,article.data,article.body,article.url])
finally:
csvFile.close()

Extract name between a word and comma from text with regex

I have a thousands .txt files with a large text. I want to extract some information from these files, like some names, numbers and dates. There are fields that follow a pattern to be able to extract the information, but others do not. I attach three examples of text.
The problem comes when I need to extract the name of the creator of a company and the name of that company. To extract the name of the creator, there is a pattern in the text. To extract the name of the company no.
The pattern is: start with the words "ante mí:" followed by a name with last names and ending in comma.
I am using the following code:
# Libreria RegEx de Python.
# coding=utf-8
import re
f = open ('/Users/anna/PycharmProjects/extractData/DiarioOficial/aaa1381582.pdf.txt','r')
mensaje = f.read()
mensaje = mensaje.replace("\n","")
print re.findall(r'\s ante mí,+[a-zA-Z]{6-24}\s', mensaje)
But I think that the regular expression is wrong.
Can someone help me?
CVE 1381582
|
Director: Juan Jorge Lazo Rodríguez
Sitio Web:
www.diarioficial.cl
|
Mesa Central:
+562 2486 3600
    Email:
consultas#diarioficial.cl
Dirección:
Dr. Torres Boonen N°511, Providencia, Santiago, Chile.
Este documento ha sido firmado electrónicamente de acuerdo con la ley N°19.799 e incluye sellado de tiempo y firma electrónica
avanzada. Para verificar la autenticidad de una representación impresa del mismo, ingrese este código en el sitio web www.diarioficial.cl
DIARIO OFICIAL
DE LA REPUBLICA DE CHILE
Ministerio del Interior y Seguridad Pública
V
SECCIÓN
CONSTITUCIONES, MODIFICACIONES Y DISOLUCIONES DE SOCIEDADES Y COOPERATIVAS
Núm. 42.031
|
Viernes 13 de Abril de 2018
|
Página 1 de 1
Empresas y Cooperativas
CVE 1381582
EXTRACTO
 
VALERIA RONCHERA FLORES, Notario Titular Décima Notaría Santiago, oficio
Agustinas 1235, piso 2, CERTIFICO: Por escritura pública hoy ante mí: DANIEL ROLANDO
CORNEJO GALLARDO, chileno, soltero, factor de comercio, con domicilio en Pueblo Hundido
sin número, comuna de Rengo, Sexta Región del Libertador Bernardo OHiggins, de paso en
ésta; constituyó sociedad por acciones denominada AGRÍCOLA Y TRANSPORTES SAN
DANIEL SpA, nombre de fantasía SAN DANIEL SpA. Objeto Social: Objeto. La Sociedad
tiene por objeto, la explotación integral del rubro agrícola, ganadero y forestal, por cuenta propia
o ajena, de predios rústicos propios o ajenos, la importación, exportación, transformación y
comercialización de productos agrícolas, ganaderos o forestales. En general, la realización de
toda clase de negocios relacionados con el agro, la producción agropecuaria, frutícola o
maderera, la compra, venta y exportación de la producción, sea la propia o ajena, servicios de
embalaje, envasado, packing y de asesoría a productores, y la elaboración de alimentos, de toda
clase a partir de esa producción; actuar como comisionista o mandatario, para la venta y
comercio de productos agrarios; realizar inversiones en toda clase de bienes muebles o
inmuebles, administrarlos y percibir sus frutos o rentas. Asimismo, el transporte de carga y/o
pasajeros, en vehículos propios, arrendados o en leasing o encomendados a terceros; la
representación de empresas extranjeras o nacionales de transporte y embalaje; todo lo
relacionado, de cualquiera forma, en la actualidad o en el futuro, con el flete, traslado y
transporte de pasajeros o bienes de toda clase y su embalaje, incluyendo las gestiones portuarias
y aduaneras, en su caso. También el ejercicio de la actividad comercial en sus formas más
amplias y, en especial, la compraventa, importación, exportación, distribución y
comercialización de toda clase de bienes, por cuenta propia o ajena, la representación de
empresas nacionales y extranjeras; y todo otro negocio o actividad conexa con el giro que
acuerden los socios. Domicilio: Comuna y ciudad de Santiago, Región Metropolitana, sin
perjuicio de que pueda establecer agencias, sucursales u oficinas en el resto del país o en el
extranjero. Duración: Indefinida. Capital: $100.000.000.- dividido en 1.000 acciones,
nominativas, de una sola serie y sin valor nominal; de las cuales el socio constituyente paga 10
acciones, equivalentes a $1.000.000.-, al contado, en efectivo, que ingresan a caja social, el saldo
de 990 acciones serán pagadas en un plazo de 3 años a contar de la suscripción del presente
contrato. Administración: Por accionista constituyente DANIEL ROLANDO CORNEJO
GALLARDO.- Demás pactos escritura extractada. "Santiago, 9 abril 2018".
Use the pattern ([^,]*) to match everything upto a comma
>>> re.findall(r'\sante mí:\s+([^,]*)', mensaje)
['DANIEL ROLANDO CORNEJO GALLARDO']

Categories