Как реализовать поисковый веб-сканер с широким и первым поиском? - PullRequest
2 голосов
/ 20 апреля 2019

Я пытаюсь написать веб-сканер на Python с Beautiful Soup, чтобы сканировать веб-страницу по всем ссылкам. После того, как я получу все ссылки на главной странице, я пытаюсь осуществить поиск в глубину и в ширину, чтобы найти 100 дополнительных ссылок. В настоящее время я просмотрел и получил ссылки на главной странице. Теперь мне нужна помощь в реализации глубины и ширины моего сканера.

Я считаю, что мой веб-сканер выполняет поиск в глубину. Это правильно или мой код неправильно выполняет поиск в глубину? Кроме того, как я могу настроить свой код для создания поиска в ширину? Я считаю, что мне нужно иметь очередь и использовать функцию pop, но я не уверен, как правильно выполнять циклы, поскольку я новичок в Python.

Я пытался откорректировать свой код, но ничего из того, что я пробовал, до сих пор не получило должного результата.

from pandas import *
import urllib.request
import re
import time
from bs4 import BeautifulSoup

#open webpage and put into soup

myURL="http://toscrape.com"
response = urllib.request.urlopen(myURL)
html = response.read()
soup = BeautifulSoup(html, "html.parser")

#get links on the main page 

websitesvisited = []
for link in soup.findAll('a'):
    websitesvisited.append(link.get('href'))

#use depth-first search to find 100 additional links

allLinks= [] 
for links in websitesvisited:
    myURL=links
    response = urllib.request.urlopen(myURL)
    html = response.read()
    soup = BeautifulSoup(html, "html.parser")
    if len(allLinks) < 101:
        for link in soup.findAll('a'):
            if link.get('href') not in allLinks:
                if link.get('href') != None:
                    if link.get('href') [0:4] == 'http':
                        allLinks.append(link.get('href'))
    time.sleep(3)

for weblinks in allLinks:
    print(weblinks)

Я соскреб веб-страницу и получил все ссылки. Теперь я ожидаю получить около 100 дополнительных ссылок, используя сканирование по глубине и ширине в ширину.

1 Ответ

3 голосов
/ 20 апреля 2019

Вы очень на правильном пути. Ключ к DFS - рекурсия, отсутствующий элемент в приведенном выше коде. Для каждой ссылки на текущей странице, рекурсивно исследуйте ее перед посещением оставшихся ссылок на странице. Используйте набор visited, чтобы отслеживать, какие страницы уже были просканированы, чтобы избежать зацикливания.

Значение "общее количество изученных ссылок", скорее всего, не поможет в DFS, поскольку ваш сканер просто снимет первую ссылку с первых 100 страниц, а затем вернется назад без какой-либо широты (почти каждая страница в Интернете имеет ссылки, поэтому терминальные узлы трудно найти). Ограничение «глубины» (или distance ) имеет больше смысла: это позволяет нам исследовать все ссылки max_depth страниц от текущей.

В любом случае код в основном такой же, и, конечно, вы можете сказать «дайте мне первые cap ссылки до max_depth страниц глубиной», если вы закодируете это в качестве базового варианта в рекурсии. Еще одна идея - убедиться, что все ссылки, которые вы изучаете, взяты с сайта quotes.toscrape. BFS будет строг в отношении изучения непосредственной границы и разветвления. Это можно сделать итеративно с очередью.

Вот рекурсивный эскиз DFS:

import requests
from bs4 import BeautifulSoup

def get_links_recursive(base, path, visited, max_depth=3, depth=0):
    if depth < max_depth:
        try:
            soup = BeautifulSoup(requests.get(base + path).text, "html.parser")

            for link in soup.find_all("a"):
                href = link.get("href")

                if href not in visited:
                    visited.add(href)
                    print(f"at depth {depth}: {href}")

                    if href.startswith("http"):
                        get_links_recursive(href, "", visited, max_depth, depth + 1)
                    else:
                        get_links_recursive(base, href, visited, max_depth, depth + 1)
        except:
            pass


get_links_recursive("http://toscrape.com", "", set(["http://toscrape.com"]))

А вот эскиз BFS:

import requests
from bs4 import BeautifulSoup
from collections import deque

visited = set(["http://toscrape.com"])
dq = deque([["http://toscrape.com", "", 0]])
max_depth = 3

while dq:
    base, path, depth = dq.popleft()
    #                         ^^^^ removing "left" makes this a DFS (stack)

    if depth < max_depth:
        try:
            soup = BeautifulSoup(requests.get(base + path).text, "html.parser")

            for link in soup.find_all("a"):
                href = link.get("href")

                if href not in visited:
                    visited.add(href)
                    print("  " * depth + f"at depth {depth}: {href}")

                    if href.startswith("http"):
                        dq.append([href, "", depth + 1])
                    else:
                        dq.append([base, href, depth + 1])
        except:
            pass

Это довольно миниатюрные наброски. Обработка ошибок и сокращение hrefs практически не рассматриваются. Существует сочетание относительных и абсолютных ссылок, некоторые из которых имеют начальную и / или конечную косую черту. Я оставлю манипулирование ими как упражнение для читателя.

...