,
,
,
...]
Notice that the number of elements is 19, not 20, because the current element is not included in the results.
>>> len(element.find_similar(ignore_attributes=['title']))
19
Get the href attribute from all similar elements
>>> [
element.attrib['href']
for element in element.find_similar(ignore_attributes=['title'])
]
['catalogue/a-light-in-the-attic_1000/index.html',
'catalogue/soumission_998/index.html',
'catalogue/sharp-objects_997/index.html',
...]
To increase the complexity a little bit, let's say we want to get all books' data using that element as a starting point for some reason
>>> for product in element.parent.parent.find_similar():
print({
"name": product.css_first('h3 a::text'),
"price": product.css_first('.price_color').re_first(r'[\d\.]+'),
"stock": product.css('.availability::text')[-1].clean()
})
{'name': 'A Light in the ...', 'price': '51.77', 'stock': 'In stock'}
{'name': 'Soumission', 'price': '50.10', 'stock': 'In stock'}
{'name': 'Sharp Objects', 'price': '47.82', 'stock': 'In stock'}
...
Advanced examples¶
See more advanced or real-world examples using the find_similar method.
E-commerce Product Extraction
def extract_product_grid(page):
# Find the first product card
first_product = page.find_by_text('Add to Cart').find_ancestor(
lambda e: e.has_class('product-card')
)
# Find similar product cards
products = first_product.find_similar()
return [
{
'name': p.css_first('h3::text'),
'price': p.css_first('.price::text').re_first(r'\d+\.\d{2}'),
'stock': 'In stock' in p.text,
'rating': p.css_first('.rating').attrib.get('data-rating')
}
for p in products
]
Table Row Extraction
def extract_table_data(page):
# Find the first data row
first_row = page.css_first('table tbody tr')
# Find similar rows
rows = first_row.find_similar()
return [
{
'column1': row.css_first('td:nth-child(1)::text'),
'column2': row.css_first('td:nth-child(2)::text'),
'column3': row.css_first('td:nth-child(3)::text')
}
for row in rows
]
Form Field Extraction
def extract_form_fields(page):
# Find first form field container
first_field = page.css_first('input').find_ancestor(
lambda e: e.has_class('form-field')
)
# Find similar field containers
fields = first_field.find_similar()
return [
{
'label': f.css_first('label::text'),
'type': f.css_first('input').attrib.get('type'),
'required': 'required' in f.css_first('input').attrib
}
for f in fields
]
Extracting reviews from a website
def extract_reviews(page):
# Find first review
first_review = page.find_by_text('Great product!')
review_container = first_review.find_ancestor(
lambda e: e.has_class('review')
)
# Find similar reviews
all_reviews = review_container.find_similar()
return [
{
'text': r.css_first('.review-text::text'),
'rating': r.attrib.get('data-rating'),
'author': r.css_first('.reviewer::text')
}
for r in all_reviews
]
Filters-based searching¶
This search method might be arguably the best way to find elements in Scrapling because it is powerful and easier to learn for newcomers to Web Scraping than learning to write selectors.
Inspired by BeautifulSoup's find_all function, you can find elements using the find_all and find methods. Both methods can take multiple types of filters and return all elements in the pages that all these filters apply to.
To be more specific:
Any string passed is considered a tag name.
Any iterable passed like List/Tuple/Set is considered an iterable of tag names.
Any dictionary is considered a mapping of HTML element(s) attribute names and attribute values.
Any regex patterns passed are used to filter elements by content like the find_by_regex method
Any functions passed are used to filter elements
Any keyword argument passed is considered as an HTML element attribute with its value.
It collects all passed arguments and keywords, and each filter passes its results to the following filter in a waterfall-like filtering system.
It filters all elements in the current page/element in the following order:
All elements with the passed tag name(s) get collected.
All elements that match all passed attribute(s) are collected; if a previous filter is used, then previously collected elements are filtered.
All elements that match all passed regex patterns are collected, or if previous filter(s) are used, then previously collected elements are filtered.
All elements that fulfill all passed function(s) are collected; if a previous filter(s) is used, then previously collected elements are filtered.
Notes:
As you probably understood, the filtering process always starts from the first filter it finds in the filtering order above. So, if no tag name(s) are passed but attributes are passed, the process starts from that layer, and so on.
The order in which you pass the arguments doesn't matter. The only order that's taken into consideration is the order explained above.
Check examples to clear any confusion :)
Examples¶
>>> from scrapling.fetchers import Fetcher
>>> page = Fetcher.get('https://quotes.toscrape.com/')
Find all elements with the tag name div.
>>> page.find_all('div')
[