我正在创建一个程序,在Wikipedia上给出起始页面和目标页面,通过每页上的超链接从起始页面导航到目标页面。例如,如果我们有凝视页面A和目标页面B,并且A链接到C,它链接到B,我们可以通过A - > C - > B从A到B.
我尝试过使用漂亮的汤,但我不熟悉网页抓取。到目前为止,我已经从页面中提取了html并对链接进行了排序。我到目前为止的代码是这样的:
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
html = urlopen("https://en.wikipedia.org/wiki/Web_Bot")
bs = BeautifulSoup(html, "lxml")
links = bs.find("div", {"id": "bodyContent"}).findAll("a",
href=re.compile("^(/wiki/)((?!:).)*$"))
for link in links:
if "href" in link.attrs:
print(link.attrs["href"])
我的问题是:如何通过页面上的链接从一个页面移动到另一个页面?
一般来说,你想要实现的并不是一项微不足道的任务。还有几个需要解决的问题。
问题1:跟踪您已访问过的所有链接以及您尚未访问的链接
问题2:知道何时停止。如果您抓取的网站非常小,那么您可以希望在一段时间后您只需找到所有可能的链接,您的抓取就会结束。
问题3:根据与哪个页面链接的知识查找路径。现在可能的解决方案:
问题1.解决方案A:使用队列和集合。在每个步骤中,将当前页面链接放入一组访问链接,从页面获取链接,检查它们中是否已存在一组访问链接,添加新链接到队列。然后从队列中选择下一个链接进行访问。非常简单的方法,特别是如果你的爬虫可以花时间。这将按顺序执行所有操作。一页接一页。
问题1.解决方案B:花1-2小时阅读Scrapy并尝试使用Scrapy实现爬行。它将为您执行多线程处理,并提供查找链接的工具(类似于您在BeautifulSoup中的操作方式)。优点:已经实现了许多功能,例如导出为CSV,JSON以进行进一步处理,记录,抓取统计信息等。
问题2.解决方案A.这取决于。如果您正在寻找任何特定路径,那么您可以在到达目标页面后立即停止,然后您可以重建从A到B的路径。
问题2.解决方案B.如果您正在寻找最短路径或能力为任何给定的A和B找到A和B之间的路径,那么您可以通过跳数限制您的爬行。假设你从页面A开始,它有到B1,B2和B3的链接。您访问它们并给它们序号1.这些B页面链接到C1,C2,C3,C4,C5 - 您访问这些页面并给它们序号2.您继续直到达到序号X,这意味着那些页面是X跳远你开始页面。这将确保您限制抓取时间。
问题3.解决方案A.当您从A页转到B1,B2,B3页面时,您“附加一个”A“的”parsel“。这意味着可以从页面A访问这些页面。您访问的每个新页面还必须保留有关可以从哪里访问的信息。然后使用DFS或BFS算法在该组链接页中查找路径。
问题3.解决方案B.您不必保留对前一页的引用,而是维护一个列表。如果您从A访问B,则您的B链接将以“A”作为其路径。但是如果你从B访问C,你将B添加到现有路径,C将包含“A-> B”,依此类推。这意味着最终每个链接都有一条从A到该链接的路径。如果您对从A到任何其他页面的任何路径感兴趣,请运行良好。
问题3.解决方案C.对于每个页面,当您从中提取所有链接时,您将构建一个地图,其中您的页面是关键,其中包含的链接列表是值。它与方法A相反,而不是孩子有父页面的引用,你有父页面列出其子页面。在这种情况下,您也可以使用DFS或WFS算法查找任意两个页面之间的路径。
废弃像维基百科这样的“巨大”网站有“巨大”的资源需求。我个人并不认为这是一项可以由资源有限的人完成的任务,或者这是一个可以在堆栈溢出答案的字数限制内明确回答的问题,即使知道如何。话虽这么说,我的答案中的以下方法可能适用于有几百页的较小网站。
做法:
master_link_dict
与key:value
对作为crawled page url
:links in that page
dictionary.keys()
中是否已有网址。target url
的页面时,我们打印出这条线索并退出。目标仅限于寻找从source url
到target url
的路径码:
import requests
from bs4 import BeautifulSoup
import re
import pprint
source_page='/wiki/Web_Bot'
target_page='/wiki/Computer_Sciences_Corporation'
master_link_dict={}
#initialize trail with target
trail_reverse=[target_page]
def get_links(url):
html=requests.get('https://en.wikipedia.org'+url)
soup = BeautifulSoup(html.text, "html.parser")
links = soup.find("div", {"id": "bodyContent"}).findAll("a", href=re.compile("^(/wiki/)((?!:).)*$"))
hrefs=[x['href'] for x in links]
return hrefs
def recursive_crawl(url):
#don't crawl again if the page has already been crawled
if url in master_link_dict.keys():
return
#get all urls in the current page
url_list=get_links(url)
#store as page:[list of urls] in the master dict
master_link_dict[url]=url_list
#if target page is found print trail
if target_page in url_list:
find_trail(url)
#crawl all urls of curret page
for item in url_list:
recursive_crawl(item)
def find_trail(url):
#append current url to trail reverse
trail_reverse.append(url)
#if current url is the source url print trail and exit
if url is source_page:
print('->'.join(trail_reverse[::-1]))
exit()
#if current url is in a page, get trail of that page
for page,url_list in master_link_dict.items():
if url in url_list:
find_trail(page)
recursive_crawl(source_page)
输出:
/wiki/Web_Bot->/wiki/Internet_bot->/wiki/Automated_bot->/wiki/Computer_science->/wiki/Computer_Sciences_Corporation
备注和免责声明:
target url
。我搜索了许多3-6页的网址进行测试。代码中的url就是其中之一。