How to get an element within an element - html

I am web scraping a real state page and trying to get the data of name, location, price, etc... in an excel table. This is the code to get that information:
soup = bs4.BeautifulSoup(driver.page_source,'lxml')
for price in soup.find_all('span',{"class":"ann-price"}):
price_list.append(price.text)
for name in soup.find_all('div',{"itemprop":"name"}):
name_list.append(name.text)
for meters in soup.find_all('div',{"class":"ann-box-info"}):
meters_list.append(meters.text)
for rooms in soup.find_all('div',{"class":"ann-box-info"}):
bedrooms_list.append(rooms.text)
for location in soup.find_all('span',{"class":"ann-info-item"}):
location_list.append(location.text)
for realtor in soup.find_all('span',{"class":"company-name"}):
realtor_list.append(realtor.text)
Questions:
Most offers have the company name as span.company-name inside a div.ann-box-contact. The problem is that whenever a special offer appears without a div.ann-box-contact (thus without company name) instead of leaving the space blank it outputs the name for the next offer, harming the excel's format.
Is there any way that I could filtrate those offers by looking for span.company-name specifically inside div.ann-box-contact?
Here is the page's code where the company name and the contact box are:
<div class="ann-box-contact">
<div class="info">
<span class="company-photo">
<span class="user-avatar"></span>
</span>
*<span class="company-name">*
<br>
The element within asterisks is the one I want to get but specifically, look for it inside the div.ann-box-contact in order to ignore any offer without Company-name.

You could find the <div class="ann-box-contact"> first and then search inside to find the <span class="company-name"> with beautifulsoup
Something like this:
div = soup.find('div', {'class':'ann-box-contact'})
span = div.find('span',{'class': 'company-name'})

Related

How to scrape text based on a specific link with BeautifulSoup?

I'm trying to scrape text from a website, but specifically only the text that's linked to with one of two specific links, and then additionally scrape another text string that follows shortly after it.
The second text string is easy to scrape because it includes a unique class I can target, so I've already gotten that working, but I haven't been able to successfully scrape the first text (with the one of two specific links).
I found this SO question ( Find specific link w/ beautifulsoup ) and tried to implement variations of that, but wasn't able to get it to work.
Here's a snippet of the HTML code I'm trying to scrape. This patter recurs repeatedly over the course of each page I'm scraping:
<em>[女孩]</em> 寻找2003年出生2004年失踪贵州省黔西南布依族苗族自治州贞丰县珉谷镇锅底冲 黄冬冬289179
The two parts I'm trying to scrape and then store together in a list are the two Chinese-language text strings.
The first of these, 女孩, which means female, is the one I haven't been able to scrape successfully.
This is always preceded by one of these two links:
forum.php?mod=forumdisplay&fid=191&filter=typeid&typeid=19 (Female)
forum.php?mod=forumdisplay&fid=191&filter=typeid&typeid=15 (Male)
I've tested a whole bunch of different things, including things like:
gender_containers = soup.find_all('a', href = 'forum.php?mod=forumdisplay&fid=191&filter=typeid&typeid=19')
print(gender_containers.get_text())
But for everything I've tried, I keep getting errors like:
ResultSet object has no attribute 'get_text'. You're probably treating a list of items like a single item. Did you call find_all() when you meant to call find()?
I think that I'm not successfully finding those links to grab the text, but my rudimentary Python skills thus far have failed me in figuring out how to make it happen.
What I want to have happen ultimately is to scrape each page such that the two strings in this code (女孩 and 寻找2003年出生2004年失踪贵州省...)
<em>[女孩]</em> 寻找2003年出生2004年失踪贵州省黔西南布依族苗族自治州贞丰县珉谷镇锅底冲 黄冬冬289179
...are scraped as two separate variables so that I can store them as two items in a list and then iterate down to the next instance of this code, scrape those two text snippets and store them as another list, etc. I'm building a list of list in which I want each row/nested list to contain two strings: the gender (女孩 or 男孩)and then the longer string, which has a lot more variation.
(But currently I have working code that scrapes and stores that, I just haven't been able to get the gender part to work.)
Sounds like you could use attribute = value css selector with $ ends with operator
If there can only be one occurrence per page
soup.select_one("[href$='typeid=19'], [href$='typeid=15']").text
This is assuming those typeid=19 or typeid=15 only occur at the end of the strings of interest. The "," between the two in the selector is to allow for matching on either.
You could additionally handle possibility of not being present as follows:
from bs4 import BeautifulSoup
html ='''<em>[女孩]</em> 寻找2003年出生2004年失踪贵州省黔西南布依族苗族自治州贞丰县珉谷镇锅底冲 黄冬冬289179'''
soup=BeautifulSoup(html,'html.parser')
gender = soup.select_one("[href$='typeid=19'], [href$='typeid=15']").text if soup.select_one("[href$='typeid=19'], [href$='typeid=15']") is not None else 'Not found'
print(gender)
Multiple values:
genders = [item.text for item in soup.select_one("[href$='typeid=19'], [href$='typeid=15']")]
Try the following code.
from bs4 import BeautifulSoup
data='''<em>[女孩]</em> 寻找2003年出生2004年失踪贵州省黔西南布依族苗族自治州贞丰县珉谷镇锅底冲 黄冬冬289179'''
soup=BeautifulSoup(data,'html.parser')
print(soup.select_one('em').text)
OutPut:
[女孩]

Insert a header from a HTML into VB

I am currently doing a school project for an elite program that serves every user's aviation purposes, from customers to pilots. I wanted to grab a text from a website called Fuel Planner. The user will input their departure and destination and then the website will load how much fuel is needed for that flight. However, I only need one part of that HTML which is the part where it prints the amount of fuel needed. The HTML code for that is as shown below:
<!-- end #menu -->
<div id="about">
<h3>Airbus A300-600-PW4158 Fuel Planner</h3>
<p>Sydney to Brisbane YSSY-YBBN (406 NM)<br></p>
<h2>Total Fuel: 26608 POUNDS</h2>
The line that I need to grab is
<h2>Total Fuel: 26608 POUNDS</h2>
I want this line to be inserted into the textbox txtFOB.Text.
Both are on the same form but on different tabs, so we don't have to worry about that. The web browser is called webFuel in the form frmPilots.
For this example, the departure ORIG is going to be
YSSY (Sydney)
And the arrival DEST will be
YBBN (Brisbane)
And the aircraft would be
A300-600
Both can be inserted into the two text boxes on the website's home page. Any help would be greatly appreciated. Thanks!
Since the element doesn't have an ID, you would need to loop thru the elements collection and look for the element you are interested in.
Try the following:
For Each element As HtmlElement In webFuel.Document.GetElementsByTagName("H2")
If element.InnerText Like "Total Fuel: * POUNDS" Then
'this is the element we are looking for...
txtFOB.Text = element.InnerText.Replace("Total Fuel:","").Replace("POUNDS","").Trim()
Exit For
End If
Next
NB: You have put a lot of tags in the question which is confusing. The above code is for VB.NET and is not tested.

Selecting dragable element based on adjacent link text - Ruby Selenium

I am automating some things for work with Selenium and I am attempting to drag a drop a list in sorted order. So I have a list of the link names and am looping through so that for each item I can drag and drop the [drag] element to the top of the page.
I can't figure out how to grab that [drag] handle element using the link_text ("Audit: " in this case).
I've tried selecting the desired element with the link_text locator and then using .attribute("id") to get the id which should be the same for the link and for the [drag] handle. The problem is that I am getting a blank string for the id instead of "evaluation_422"
reversed.each do |form|
puts form
eval = browser.find_element(link_text: "#{form}")
id = eval.attribute("id")
puts id
#returns blank string
end
Any help is greatly appreciated! Below is the HTML I am trying to reference.
<li id="evaluation_422" class="evaluation">
<div class="right">
<span class="handle">[drag]</span>
<a href="/evaluations/422/edit">
<span>Audit: </span>
</a>
Okay so I finally solved this issue by grabbing the href of the link which happened to contain the number of the evaluation, so I was able to select the correct handle element using this:
reversed.each do |form|
href = browser.find_element(link_text: form).attribute("href")
eval_num = href.gsub("https://thisistheurl.com/evaluations/","").gsub("/edit","")
handle = browser.find_element(xpath: ".//*[#id='evaluation_#{eval_num}']/span")
top = browser.find_element(class_name: "evaluation")
browser.action.drag_and_drop(handle, top).perform
end

Trouble with Xpath in Google Spreadsheets (ImportXML)

This is a great site, and I've already had a lot of questions answered simply by scrolling and searching through other postings. Unfortunately, I can't seem to track down an answer that specifically helps this problem, and figured I would try posting and looking for help-
I'm using ImportXML and google spreadsheets to 'scrape'a few product descriptions from a retail site. It's been working fine for the most part, and I have done it in 2 ways:
1) Specific call to the description part of a post:
=ImportXML(A1,"//div[#class='desc']")
2) Call to the entire 'product Card', which also returns info such as product title, price, time posted, and places these items in adjacent cells in my Google spreadsheet:
=ImportXML(A1,"//div[#class='productCard']")
Both have worked fine, but I've ran into a different problem using each method. If I can resolve even one of these problems, then I'll happily scrap the other method, I just need one of them to work. The problems are:
Method 1) The website prohibits sellers from including contact information in product postings-- when they include an email address anyways, the site automatically blocks it, so that in the posting it simply appears as "...you can reach me at [obscured]" or something like that. The [obscured] appears in a different colour text and is obviously treated differently somehow. When I scrape these descriptions using Method 1, ImportXML appears to get 'bumped' when it hits the word [obscured], and it passed the remaining text from that product description to the next cell over in my spreadsheet. This ruins the entire organization of the sheet, and I'd like to find a way where I can get ImportXML to just ignore the [obscured], and still place the entire text of the product description in one cell.
Method 2) My call for the entire 'product Card' is as follows:
=ImportXML(A1,"//div[#class='productCard']")
As mentioned, this works fine (for most products), and I don't mind the additional info (price, date, etc.) being posted in adjacent cells.
However, the website also allows certain products to be 'featured', where they appear in a different colour box on the site, and are therefore more likely to get a buyer's attention.
Using this method, the 'featured' products are not scraped or imported into my spreadsheet, but are simply passed over.
The source code (on actual site) (via 'inspect element' in Safari) for both the description (Method 1) and product card (Method 2) look as follows (for a normal product (a) and a featured product (b)):
(a)
<div id="productSearchResults">
<div class="productCard tracked">
<div>...</div>
<div class="stats">...</div>
<div class="desc collapsed descFull">...</div>
</div>
(b)
<div id="productSearchResults">
<div class="productCard featured tracked">
<div>...</div>
<div class="stats">...</div>
<div class="desc collapsed descFull">...</div>
</div>
You can see in both (a) an (b) the 'desc' class that I call in Method 1, which seems to work fine.
From my reading on this site, I think I've learned that a given class can't have more than one word, and therefore the use of "desc collapsed descFull" and "productCard tracked" and "productCard featured tracked" don't represent classes with 3, 2 and 3 words in the title, but instead cases where multiple classes have been assigned?
Regardless, the call to 'desc' (Method 1) works fine and seems to get all descriptions.
In method 2 therefore, I would have thought that a call to 'productCard' would get the info for all products, both featured and regular, as 'featured' is an extra class assigned to some 'productCard's. If I call all 'productCard's, shouldn't the normal AND featured ones be returned? This is currently not the case. I've tried calling just 'tracked' and just 'featured' as classes, and neither returns anything, so my logic that they are their own class equivalent to 'productCard' may be flawed.
In summary, the 'desc' call in Method 1 works fine, and even gets descriptions for 'featured' products. However, when contact information is included in the description and is displayed as [obscured] it bumps my data into the next cell in the spreadsheet, immediately following the word. This throws off and ruins all organization.
In Method 2, I am not getting the featured products at all, which greatly weakens what I am trying to do. Can either (or both!) of these problems be fixed??
Thanks so so much for any help you can give me.
***UPDATE: As seen in the comments below, use of the 'contain' as suggested improved Method 2 by retrieving both regular and featured products. However, featured product cards have extra text elements, and since the entire card is being scraped in this method, featured products do not match the cell alignment that regular products do. If there is a way to fix Method 1, this would therefore be much better.
As outlined in the comments below, the [obscured] text appears in a 'span' that follows underneath/indented from the
<div class="desc descFull collapsed"
as
<span class="obscureText">[obscured]</span>
Is there any way that I can import the 'desc's as I have been, but tell the XPath to essentially 'ignore' the [obscured] span, or at least deal with it in a way that doesn't make description text immediately after [obscured] appear one cell over?
Thanks so much everyone!
You can wrap your function with the concatenate()-function to make sure it all shows up in one cell:
=concatenate(ImportXML(A1,"//div[#class='productCard']"))

Excel Vba – how verify html child element is there or not

I am making excel file to get product price from site and compare. So far i managed to parse product name and price. but problem comes when product is on sale, then it had different element as shown below 1 is normal 2 is on sale
1.
<div class="price">
<span>$87</span>
</div>
2
<div class="price">
<del>100</del>
<ins>80</ins>
</div>
I am doning
Set hPrice = hPord(r).getElementsByClassName("price")
for loop
ActiveSheet.Range("H6").Offset(r, 0).Value = hPrice(0).innerText
this work fine for normal product price but on sale product it returns "100 80"
i try to use
If Not hPrice(0).getElementsByTagName("ins") Then
this gives error when "ins" is not present,
pleae let me know how to verify child tag is there or not, or you have better alternative
Thanks
You have forgotten to add index of <ins> tag to your line. This could be something like this:
If Not hPrice(0).getElementsByTagName("ins")(0) Then
in other words, to get the product price value you need to have this line:
hPrice(0).getElementsByTagName("ins")(0).innerText
Try below samples
If Not hPrice.getElementsByTagName("ins")(0) Then
OR
If Not hPrice.getElementsByTagName("ins") Then