Aplica alguna de las indicaciones recomendadas en este podcast sobre el codigo. El podcast: "Welcome back, everyone. Ready to dive into something. I bet a lot of you are dealing with this like every single day, making those web searches like super efficient. Right. So picture this. Okay. You're building a Python app. Okay. It's going to crawl a website. It's going to be pulling out text, following links, kind of like your own like miniature search engine. Right. But there's a twist. This app, it's got to stick to a specific section of the website. Okay. Got to respect those boundaries. Interesting. Set by a user-provided base URL. Okay. And of course, we want it to be like lightning fast. Of course. Of course. And smart about which links it's, you know, actually going to follow. Absolutely. Now, to help us with this little challenge, we've got two awesome sources. Okay. First up, we've got a snippet of the actual Python code itself. Oh, cool. Yeah. It might look a little intimidating at first glance. I bet. But no worries. Our expert is here to break it all down. Love to do that. And second, we've got this brainstorming document just overflowing with optimization ideas. I love that. Almost too many. Almost too many. Which is where your insights are going to be like invaluable. Sounds good. So your job, dear listener, dear expert, is to help us sift through all of this and figure out like what's going to make the biggest impact. Absolutely. Are you ready to optimize? Let's do it. All right, let's dive into the code and see where we can inject some serious efficiency. Sounds good. Okay, so at first glance, this code, it's pretty sophisticated. It is, yeah. It uses libraries like Requests, Beautiful Soup, even Told Extract. Yeah. Definitely not messing around here. Yeah, you're going for it. But what jumps out at you right away as potential areas for, you know, improvement? So the first thing that I see is the link selection process. Okay. It's already filtering by domain name, which is great. That makes sense. For staying within the website. Right. But we can make it even smarter by prioritizing the right links within that domain. I see. I see where you're going with this. Yeah. We don't want just any link. We want the ones that are like most relevant to what the user is actually looking for. Exactly. I like it. And that's where this brainstorming doc comes in handy. It mentions a few different strategies for prioritizing links. One idea is to look for keywords related to the user's query. Oh. So it's like a targeted search within the website. So if the user's searching for info on, let's say, Python web scraping, then we're going to bump up those links that have those keywords, even if they're buried deep within the allowed domain. Exactly. I like that. Another strategy is to prioritize the links based on where they are on the page. Oh, interesting. So the links in like prominent positions, like the navigation menus, are often more important than the ones like tucked away in the footer or something like that. Yeah, like you're understanding the hierarchy of information. Exactly, exactly. Knowing where to look for the really good stuff. You got it okay now looking back at the code we can see that this extract links function this is where like the magic happens okay and it's already designed to filter links but we could add like another layer of filtering based on keywords or location. So we're layering our filters. Exactly. First by domain, then by keyword or location. Yeah. Kind of like a funnel. Right. Exactly. Arrowing it down to the really promising links. Exactly. But there's more to optimization than just like picking the right links. Right. We also need to avoid wasted effort. You mean like going down rabbit holes of irrelevant links. Exactly. Yeah, no one wants to do that. Nobody wants that. Waste time. So thankfully, the code already remembers the pages that's visited. Okay. So we're not going to get stuck in like these endless loops. Oh, good. But we can take this even further with caching. Caching? Is that like saving a web page on your computer so you don't have to download it again? That's a great analogy. Okay. So the code already has a basic caching system using a dictionary. Okay. And it's storing the extracted text and links for each URL. So we don't have to refetch them every time. Okay. But we could look at different caching strategies or data structures to make this even more efficient. So we're being smart about what we download and what we store. Just maximizing that efficiency at every step. Exactly. Exactly. I like it. But being efficient also means being respectful, right? Oh, okay. We need to make sure that our crawler is playing by the rules of the website. Right, right. We don't want to be that annoying guest who overstays their welcome. Exactly. Exactly. Websites often have this robots.txt file that outlines which parts of the site crawlers are allowed to access. Our current code doesn't take this into account. So we need to add something in there to respect those rules. So we could create a function to check that robots.txt file before we even try to To respect those rules. So we could, like, create a function. Exactly. To check that robots.txt file before we even try to access any page. Exactly. Make sure we're not, like, stepping over any boundaries. That's a great idea. Yeah. It's a simple addition, but it goes a long way in being a good internet citizen. A good internet citizen. I like that. Right. So, so far we've focused on smart link selection. Avoiding redundancy. Yeah. Being respectful of those website rules. Uh-huh. What other areas of this code could we optimize? Well, one area that's often overlooked is controlling how deep and how far a crawler actually reaches. Sometimes you can get lost following link after link after link, which can be incredibly time-consuming. Yeah, yeah. So it's like setting boundaries, right? We want to make sure that the crawler is staying focused on the relevant parts of the website. Makes sense. So we could set a depth limit, meaning the crawler can only follow links to a certain level within the site. So if we set a depth limit of three, it would only explore links that are like up to three clicks away from the base URL. You got it. Okay. Okay. And we could even restrict the crawler to specific sections of the website. Very interesting. For instance, if we know that the most relevant content is under, let's say, the blog directory, we can tell the crawler just focus on links within that path. Okay. It's all about that laser focus. Laser focus, making sure our little crawler friend doesn't wander off. Exactly. Into irrelevant territory. Precisely. Okay. Now let's look at how our application is interacting with the server. Okay. Currently, it pauses for five seconds after each request, which is, you know, common practice to avoid, like, overwhelming the server. Right, right. But it also means our crawlers spending a lot of time just waiting around. Oh, okay. Like being stuck in traffic when there are faster routes available. That's a great analogy. Yeah. The brainstorming document suggests implementing concurrent requests. Okay. This means sending multiple requests at the same time, which can really speed things up. So instead of like one car on the road, we've got a whole convoy moving in parallel. Exactly. Python libraries like Ascensio or Juvent can help us do this, and we could modify the code to send out a batch of requests all at the same time and process those responses as they come back. So we're no longer limited by the speed of a single request. Got it. That's a major efficiency boost. It is. Okay. But remember, we still need to be mindful of the server. Right. We could start by gradually increasing the number of concrete requests and kind of monitor how the server's responding and find that sweet spot between speed and respect. So it's about finding the right balance, maximizing speed without causing any trouble. Exactly. Clever, clever. What other tricks do we have up our sleeve? What other tricks indeed? Well, we can also try to minimize the amount of data that we're transferring with each request instead of. Instead of downloading the entire webpage every single time. Yeah. We could just request the specific information that we need. Oh, smart, smart. It's all about being mindful of resources. Yeah. Both ours and the servers. Speaking of resources. Yeah. I'm curious about how the code is handling the extracted text. Okay. It seems to be storing everything in like this big list. Oh, you're referring to the self.texts variable. Yeah. Yeah, exactly. Yeah. It's acting as like a repository for all that text content. Right. But if we're dealing with a massive website, that list could get like really huge. Wouldn't it be more efficient to have like some kind of indexing system in place? You're thinking ahead, which is great. Yeah. Storing all of the text in memory could be like a bottleneck as the application scales. Okay. And you're right. Having an efficient search mechanism is crucial when you're dealing with lots of data. So what are some like alternatives we could consider? Maybe like a database or something. A database is a great option. Databases are designed for efficient storage and retrieval of large amounts of data. Right. We could create a table with columns for the URL, the extracted text, and maybe some other metadata, like the date that the page was crawled. And most databases offer those powerful indexing capabilities. So we could like really quickly search for specific keywords or phrases within all that text. Yeah. Using a database also gives us more flexibility in accessing and processing the data. We could easily export it for like further analysis or use it to power other applications. I'm starting to like see the bigger picture here. Yeah. It's not just about building this web crawler. It's like it's about creating this foundation for a whole ecosystem of tools and applications. Exactly. The possibilities are endless. Endless indeed. When you start thinking about data in this way. I like it. But for now, let's keep our focus on optimizing this web crawler. Okay. We've made some great progress already. We have. But there's always more to explore. This has been incredibly insightful so far. That's good. I'm excited to see what other optimizations we can unearth. Me too. As we continue our journey. Let's take a closer look at that extract links function again. Okay. I have a feeling there are even more ways to be smarter about which links we choose to follow. I'm all ears. Let's unlock even more efficiency in part two. Let's do it. So, you know, we've been talking a lot about filtering those links based on keywords, location, all that good stuff. Right. But what if we dig even deeper? Deeper. Okay, I'm intrigued. What do you have in mind? What if we actually analyze the structure of the URLs themselves? Oh, okay. Maybe certain patterns or elements within the URL, it can give us clues about the content. Oh. They point to... So instead of just looking at the keywords, we're actually analyzing the anatomy of the URL. Exactly. Okay. For instance, maybe URLs containing specific keywords in the path or pointing to certain file types, those are more likely to lead to really valuable content. Oh, I see what you mean. Like URLs ending in, let's say,.pdf might be more likely to be research papers or something. Totally. While like ones with blog in the path. Yeah. Could indicate like blog articles. Exactly. Okay. And we can use this insight to create a whole scoring system for links. Okay. URLs matching these patterns would get a higher score, bumping them up in the priority queue for crawling. It's like we're teaching the crawler to recognize those subtle hints within the URL structure itself. Exactly. We're going beyond simple rules and incorporating more nuanced patterns for intelligent link selection. I love it. So we've got a plan for smart link prioritization. What about the actual process of fetching those web pages? Yeah. Any room for optimization there? Absolutely. Remember how we talked about minimizing data transfer? Yeah. There's this technique called conditional GET. Okay. That can be a real game changer. Conditional GET. Okay. Tell me more. So basically we send a special header along with our request. Okay. That can be a real game changer. Conditional G-E-T. Okay. Tell me more. So basically, we send a special header along with our request. Okay. That asks the server, hey, has this page changed since the last time I visited? Okay. If the content's the same, the server just sends back a 304 not modified response. Uh-huh. And we don't have to download the entire page again. Oh, so it's like checking for a newer version of a file. Exactly. Before you download it. Exactly. Makes perfect sense. And this checking for a newer version of a file before you download it. Exactly. Makes perfect sense. And this can save us a ton of time and bandwidth. Right. Especially when revisiting pages that don't change that often. Smart. We're only downloading what we absolutely need. Exactly. Maximizing efficiency at every turn. You got it. I like it. All right. Now, shifting gears a bit, I remember the brainstorming document. I had this whole section on efficient resource management. Yes. What are some key considerations there? Resource management is crucial. Right. It's not just about speed. It's about how efficiently we're using the available resources, especially memory. If our application is gobbling up too much memory, it can slow down the whole system. Oh, yeah. Or worse, crash. Yeah, that's a disaster. Exactly. We want to avoid at all costs. Yes. So how do we make sure that our code is a responsible memory citizen? Well, the good news is the code already uses a caching mechanism, which is fantastic. Right. It's storing frequently accessed data in memory. So we're reducing redundant fetches and saving that precious memory space. So it's like having a designated shelf for the most used ingredients in our recipes for web crawling. I love that analogy. Okay, good. And the code uses a dictionary for the cache. Yeah. Which is a great choice. Because dictionaries are really efficient for lookups. Okay, so we're off to a good start. Yeah. But are there any other tricks we can use to keep that memory usage in check? We could explore techniques like garbage collection. Oh, okay. Which is kind of like having a cleanup crew. Okay. That automatically frees up memory that's no longer being used. So it's like tidying up your workspace as you go. Exactly. Makes sense. Yeah, keep everything neat and organized. Another approach is to process data in smaller chunks. Okay. Rather than loading everything into memory at once. No, it's like breaking down a giant feast. Exactly. Into manageable portions. Precisely. Make sure we don't overload our system. These techniques can really impact performance. Exactly. To manageable portions. Precisely. Make sure we don't overload our system. These techniques can really impact performance. Okay. Especially when dealing with like massive data sets. Yeah, that makes sense. It's all about being mindful of how our code is using memory and finding ways to be as thrifty as possible. Okay. So we've discussed optimizing various aspects of the crawler from link selection to data fetching to resource management. But what about incorporating some of those those cutting edge AI techniques we hinted at earlier? Ah, yes. Artificial intelligence. Yeah. It can truly revolutionize web crawling. OK. web crawling. Okay. Imagine if our crawler could learn from its experiences. Okay. Identifying patterns in the types of links that lead to the most valuable content. That would be incredible. Right? It's like having this super smart assistant. Yeah. That learns your preferences. Totally. And guides you toward the most relevant information. Exactly. And machine learning models can help us do that. For example, we could use a classifier to predict the likelihood that a link will lead to content that's relevant to the user's query. So instead of relying solely on keywords, we're training the crawler to understand the context and the meaning behind those keywords. Exactly. And what's great about machine learning is it just keeps getting better over time. Yeah. As it gathers more data. Like having a crawler that gets smarter and more efficient with each crawl. Exactly. Now, that's what I call next level optimization. Right. We've gone from basic filtering to like intelligent link prediction. We've come a long way. We have. We have. And while the current code doesn't incorporate any AI techniques yet, it's definitely something to consider for future enhancements. Absolutely. It's exciting to see how AI can, like, elevate our web crawler from a simple rule-based system to a truly intelligent information discovery tool. It really is. It's a testament to, you know, continuous improvement and embracing new technologies. I couldn't agree more. Good. So far, this deep dive has been like a goldmine of optimization strategies. It has. I'm eager to see what other gems we unearth as we continue our exploration. Me too. Ready for part three. Absolutely. Let's wrap up this optimization journey with some final insights. And leave our listener with some food for thought. Okay. So we've covered a ton of ground in this deep dive. We have. Smart link selection, caching, resource management. We even touched on the potential of AI. We did. It feels like we've assembled like a powerful toolkit. We have. For optimizing our web crawler. It's true. What's remarkable is how each of these optimizations, they kind of build on each other. They do. Creating this like compounding effect on the overall efficiency. Yeah. It's like we've been fine tuning a high performance engine. Oh. Making it faster, smoother, more powerful with every adjustment. I like that analogy, yeah. And just like a finely-tuned engine, it needs regular maintenance. Right. Our web crawler, it's going to benefit from that continuous monitoring and adjustments. So it's not just about setting it and forgetting it. Yeah. We need to keep an eye on how it's performing and make tweaks as needed. Exactly. The brainstorming document, it really highlights those feedback mechanisms. Okay. Imagine if our crawler could actually track which links lead to the most engaging content. Right. Maybe by analyzing how long users spend on a page or how often they're sharing it. We could use that feedback to refine our link selection even more, giving higher priority to the links that have historically led to high user engagement. Exactly. It's like a built-in quality control system that helps our crawler learn and adapt. So speaking of learning and adapting, I remember the brainstorming document mentioned this idea of automatic feedback. Yes. Almost like the applications of learning as it goes. Could you elaborate on that? Absolutely. So it's all about closing that feedback loop. Okay. So imagine if our crawler could track those user interactions with the content that it finds. Okay. Let's say it notices that users are consistently spending more time on pages. With a specific layout or topic, it can then adjust its link selection algorithm to prioritize pages with similar characteristics. So it's like the crawler develops this sense of what users find valuable. Exactly. And then adjusts this behavior accordingly. And this is where machine learning can really shine. Okay. We can train algorithms to analyze those user behavior patterns and predict which links are most likely to lead to engaging content. It's like having a personal research assistant. Totally. That constantly refines its search strategy based on your preferences. Precisely. And while implementing this type of system might take a bit more development effort, the potential gains in efficiency and relevance, I mean, they're huge. I can already picture a future where web crawlers are highly personalized, delivering exactly the information that each user needs without any wasted effort. It's a great vision. And while we may not be there yet, the building blocks are there. That's exciting. So let's bring it back to the code. Okay. I wanted to highlight one specific detail I found really insightful. Oh, I'd love to hear it. Remember how we were talking about staying within those boundaries? Yes. Set by that base URL? Well, I noticed the updated code includes this function called isValidryl. Oh, nice. It's designed to ensure the crawler only follows links within that allowed domain. It's a crucial addition. It is. It really reinforces that we're not just blindly following any link. We're being very careful to stay focused and efficient and what's what's really neat is is how this function works yeah how does it work it it compares the path segments of that that candidate url with the base url right and if there's a mismatch it rejects the link link. Prevents the crawler from, you know, straying off course. That's a really clever way to enforce those boundaries. Make sure our crawler's behaving responsibly. It's a fantastic example of how even those small targeted optimizations can have a big impact on the overall performance and effectiveness of what we're building. Absolutely. It's really about thoughtful design and attention to detail. Well, this deep dive has been an incredible journey. We've explored basic filtering and caching to the potential of AI. We did. I think the key takeaway here is that optimization, it's an ongoing process. It is. It's about constantly evaluating, identifying areas for improvement, embracing new technologies and techniques. It's a journey of continuous learning and refinement. It is. Always striving to make our web crawler better, faster, and smarter. And that's what makes software development so engaging and rewarding. Couldn't have said it better myself. Good. So to our listener, we encourage you to keep exploring. Yes. Keep experimenting. Please. Keep pushing the boundaries of what encourage you to keep exploring. Yes. Keep experimenting. Please. Keep pushing the boundaries of what's possible with web crawling. Absolutely. Optimization is not a destination. It's a journey. Happy crawling. And until next time, keep diving deep."
El Codigo:
"import requests
from bs4 import BeautifulSoup
import tldextract
import time
import random
import sys
from urllib.parse import urljoin, urlparse
from colorama import Fore, Style, init
from difflib import SequenceMatcher
import requests_cache
import textstat
from collections import deque  # Importar deque para una cola más eficiente

# Initialize colorama for colored output
init(autoreset=True)

requests_cache.install_cache('web_cache', expire_after=180)  # Cache por 3 minutos


class WebNavigatorGame:
    def __init__(self, start_url, duration_seconds=60):
        self.start_url = self.normalize_url(start_url)
        self.domain = self.get_domain(self.start_url)
        self.duration = duration_seconds
        self.visited = set()
        self.to_visit = deque()  # Cambiado a deque para eficiencia
        self.texts = []
        # Cache to store extracted data: {url: {'text': ..., 'links': set(...)}}
        self.cache = {}
        # Define headers to simulate a human browser
        self.headers = {
            "User-Agent": (
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/112.0.0.0 Safari/537.36"
            ),
            "Accept-Language": "en-US,en;q=0.9",
            "Accept-Encoding": "gzip, deflate",
            "Connection": "keep-alive",
        }
        # Store base URL path segments
        self.base_url = self.start_url
        self.base_path_segments = self.get_path_segments(self.base_url)

    def normalize_url(self, url):
        """Ensure the URL has a scheme."""
        parsed = urlparse(url)
        if not parsed.scheme:
            return "http://" + url
        return url

    def get_domain(self, url):
        """Extract the domain using tldextract."""
        extracted = tldextract.extract(url)
        domain = f"{extracted.domain}.{extracted.suffix}"
        return domain.lower()

    def get_page(self, url):
        """Fetch the content of a web page with headers."""
        for attempt in range(3):  # Intentar hasta 3 veces
            try:
                response = requests.get(url, headers=self.headers, timeout=10)
                response.raise_for_status()
                return response.text
            except requests.exceptions.Timeout:
                print(Fore.RED + f"Timeout occurred for {url}. Retrying...")
            except requests.RequestException as e:
                print(Fore.RED + f"Failed to retrieve {url}: {e}")
                break  # Salir si no es un error temporal
        return None

    def extract_text(self, html_content):
        """Extract text from HTML content."""
        soup = BeautifulSoup(html_content, 'lxml')
        for script_or_style in soup(['script', 'style']):
            script_or_style.decompose()
        text = soup.get_text(separator=' ', strip=True)
        return text

    def get_path_segments(self, url):
        """Return the list of path segments for a URL."""
        parsed = urlparse(url)
        segments = [segment for segment in parsed.path.split('/') if segment]
        return segments

    def is_valid_url(self, url):
        """Check if the URL satisfies the constraints."""
        url_segments = self.get_path_segments(url)
        base_segments = self.base_path_segments
        if len(url_segments) < len(base_segments):
            return False
        for base_seg, url_seg in zip(base_segments, url_segments):
            if base_seg != url_seg:
                return False
        return True

    def extract_links(self, html_content, base_url):
        """Extract all unique, valid links from HTML content."""
        soup = BeautifulSoup(html_content, 'html.parser')
        links = set()
        for link in soup.find_all('a', href=True):
            href = link['href']
            full_url = urljoin(base_url, href)
            normalized_url = self.normalize_url(full_url)
            if self.get_domain(normalized_url) == self.domain:
                parsed = urlparse(normalized_url)
                clean_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
                if self.is_valid_url(clean_url):
                    links.add(clean_url)
        return links

    def get_extracted_data(self, url):
        """Retrieve extracted text and links from the cache or extract them if not cached."""
        if url in self.cache:
            return self.cache[url]['text'], self.cache[url]['links'], False
        else:
            html_content = self.get_page(url)
            if html_content is None:
                return None, set(), True
            text = self.extract_text(html_content)
            links = self.extract_links(html_content, url)
            self.cache[url] = {'text': text, 'links': links}
            return text, links, True

    def is_text_unique(self, new_text):
        """Check if the new text is more than 50% different from existing texts."""
        for old_text in self.texts:
            similarity = SequenceMatcher(None, new_text, old_text).ratio()
            if similarity > 0.5:
                return False
        return True  # It's unique enough

    def is_text_readable(self, text, threshold=60):
        """Check if the text is readable based on the Flesch Reading Ease score."""
        if not text:
            return False
        score = textstat.flesch_reading_ease(text)
        print(f"Flesch Reading Ease score: {score}")
        return score >= threshold

    def run(self):
        """Run the web navigation game."""
        start_time = time.time()
        current_url = self.start_url
        self.to_visit.append(current_url)

        while time.time() - start_time < self.duration and self.to_visit:
            current_url = self.to_visit.popleft()  # Usar popleft con deque
            if current_url in self.visited:
                continue  # Ya visitado, saltar

            print(Fore.CYAN + f"\nVisiting: {current_url}")

            text, links, request_made = self.get_extracted_data(current_url)
            if text is None:
                self.visited.add(current_url)  # Marcar como visitado incluso si falla
                continue

            if not self.is_text_readable(text) and current_url != self.start_url:
                print(Fore.YELLOW + "Text is not readable. Extracting links only from this URL.")
                # Agregar enlaces incluso si el texto es ilegible
                new_links = links - self.visited
                self.add_to_visit(new_links)
                self.visited.add(current_url)
                continue  # Saltar el procesamiento relacionado con texto

            if request_made:
                if self.is_text_unique(text):
                    self.visited.add(current_url)
                    self.texts.append(text)
                    print(Fore.GREEN + f"Extracted Text (first 200 chars):\n{text[:200]}...\n")
                else:
                    print(Fore.YELLOW + "Text is not unique enough. Skipping text extraction for this URL.")
                    self.visited.add(current_url)
                    continue  # Saltar al siguiente URL
            else:
                print(Fore.BLUE + f"Revisited {current_url}. Using cached data.\n")

            new_links = links - self.visited
            self.add_to_visit(new_links)

            if request_made:
                print(Fore.MAGENTA + "Waiting for 5 seconds before the next request...\n")
                time.sleep(5)

        print(Fore.MAGENTA + f"\nGame Over! Visited {len(self.visited)} unique pages.")
        self.save_texts()

    def add_to_visit(self, new_links):
        """Helper to add new links to visit, ensuring no duplicates."""
        filtered_links = set()
        for link in new_links:
            if link not in self.visited:
                filtered_links.add(link)
        if filtered_links:
            self.to_visit.extend(filtered_links)
            random.shuffle(self.to_visit)
        else:
            print(Fore.YELLOW + "No new links found on this page.")

    def save_texts(self, filename="extracted_texts3.txt"):
        """Save all extracted texts to a file."""
        with open(filename, 'w', encoding='utf-8') as f:
            for idx, text in enumerate(self.texts, 1):
                f.write(f"--- Page {idx} ---\n")
                f.write(text + "\n\n")
        print(Fore.BLUE + f"All texts have been saved to {filename}.")


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python web_navigator_game.py <start_url> <duration_seconds>")
        print("Example: python web_navigator_game.py https://example.com 120")
        sys.exit(1)

    start_url = sys.argv[1]
    try:
        duration = int(sys.argv[2])
    except ValueError:
        print("Duration must be an integer representing seconds.")
        sys.exit(1)

    game = WebNavigatorGame(start_url, duration)
    game.run()"