Scraping Website With Beautiful Soup Library in Django Project
Create a simple scraper to get information from a web page. With BeautifulSoup & Requests libraries to analyze Web Page using text formatting HTML html.parser. Extract data of links referenced in this web page. This code pre-processes data and sends to the template an object that contains a list of anchors. Render scraped data of all links in this web page.
Scraped Data
Show Code
views.py Code
def scrape_data(request):
# URL of the web page you want to scrape
url = "https://portfolio-mparraf.herokuapp.com"
search_line = "def scrape_data(request):"
pattern = "def xml_books(request):"
try:
with open(__file__, 'r') as file: # este módulo se documenta a sí mismo
content = file.readlines()
index = None
for i, line in enumerate(content):
if search_line in line and index is None:
index = i
if line.startswith(pattern) and index is not None:
pos = i
# static_dir apunta al directorio real de estáticos (mparraf/static).
content_path = os.path.join(static_dir, 'json/data-science/content.json')
with open(content_path, 'r') as file:
content_json = file.read()
content_raw = json.loads(content_json)
content_filter = {key: value for key, value in content_raw.items() if key.startswith("scrape")}
except FileNotFoundError:
# Sin la metadata la página sigue siendo útil: se muestran los enlaces
# y el código, solo falta el texto descriptivo.
content_filter = {}
views = content[index: pos]
# Send an HTTP GET request to the URL. The page being scraped is this very site, so the
# HTML is cached for 15 minutes: one fetch per window instead of one per visitor, and
# the demo cannot pile requests onto the server that is rendering it.
def fetch_home():
response = requests.get(url, timeout=20)
return response.content if response.status_code == 200 else None
html = cache.get_or_set("scrape_demo:home_html", fetch_home, 60 * 15)
if html is not None:
# Module to analyze files with text formatting HTML.
soup = BeautifulSoup(html, 'html.parser')
# Extract data of links referenced in this web page. This code pre-processes data and sends to the template an object that contains a list of anchors.
links = [{'data': url + link.get('data'), 'title': link.get('title')} for link in soup.find_all('object')]
# Render scraped data of all links in this web page.
context = {'links': links, 'views': views, 'content': content_filter}
return render(request, 'data-science/scrape/scraped_links.html', context)
else:
# Handle the case where the request was not successful
return render(request, 'data-science/scrape/scraper_error.html')
# start xml
import os
import io
import ssl
import unicodedata
import requests
import xml.etree.ElementTree as ET
from urllib.parse import urlencode
from django.core.cache import cache
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.template.exceptions import TemplateDoesNotExist
from django.shortcuts import render
def _norm(s: str) -> str:
"""Normaliza texto: minúsculas, sin acentos, espacios compactados."""
if not s:
return ""
s = unicodedata.normalize("NFKD", s)
s = "".join(c for c in s if not unicodedata.combining(c))
return " ".join(s.lower().strip().split())
def _fetch_books_catalog(file_id):
"""Descarga books.xml desde Drive y devuelve el catálogo completo normalizado.
Es la parte cara (~12 MB de descarga + parseo de ~10.000 items), así que su
resultado se cachea; los campos *_norm precomputados dejan el filtro por
request en una pasada barata sobre la lista.
"""
xml_bytes = download_gdrive_public_file(file_id)
root = parse_xml_bytes(xml_bytes)
catalog = []
# Recorremos items; las etiquetas son <item>, <auth>, <book>, etc.
for node in root.findall(".//item"):
def tx(tag):
el = node.find(tag)
return (el.text if el is not None and el.text is not None else "").strip()
author_raw = tx("auth")
title_raw = tx("book")
img_raw = tx("img_url")
if img_raw and not img_raw.startswith(("http://", "https://")):
img_raw = "https://" + img_raw.lstrip("/")
catalog.append({
"id": tx("isbn"),
"title": title_raw,
"title_norm": _norm(title_raw),
"author_norm": _norm(author_raw),
"language": tx("lang").capitalize(),
"price": tx("euro"),
"publish_date": tx("year"),
"description": tx("about"),
"publisher": tx("publ").capitalize(),
"tags": tx("tags"),
"img": img_raw,
"page": tx("page"),
})
return catalog
Template