本教程将涵盖以下内容:
- 为什么要抓取GitHub仓库?
- GitHub抓取库和工具
- 使用Beautiful Soup构建GitHub仓库抓取器
为什么要抓取GitHub仓库?为什么要抓取GitHub仓库?
抓取GitHub仓库有几个原因。最流行的原因是:
- 跟踪技术趋势:通过跟踪仓库的星标和发布,你可以了解编程语言、框架和库的当前趋势。抓取GitHub可以分析哪些技术正在流行,以监测它们的增长并识别新兴趋势。这些数据可以指导关于技术采用、技能发展和资源分配的决策。
- 获取丰富的编程知识库:GitHub是开源项目、代码示例和解决方案的宝库。这意味着你可以从平台上收集大量编程知识和最佳实践。这对于教育目的、提高编码技能和理解不同技术的实现方式非常有用。
- 获得协作开发的见解:仓库提供了开发者如何通过拉取请求、问题和讨论进行协作的见解。通过收集这些数据,你可以研究协作模式,以帮助制定团队合作策略、改进项目管理和完善软件开发流程。
GitHub不是唯一的基于云的git仓库托管平台,还有很多替代品。然而,由于以下原因,它仍然是数据抓取的首选:
- 庞大的用户群
- 高用户活跃度
- 建立的声誉
特别是,GitHub数据对于监控技术趋势、发现库和框架以及改进软件开发过程非常有价值。这些信息在IT世界中保持竞争优势起着关键作用。
GitHub抓取库和工具
Python被广泛认为是进行网页抓取的优秀语言,因为它的语法简单、对开发者友好以及丰富的库。在我们的深入指南中了解更多关于如何使用Python进行网页抓取的内容。
下一步是从广泛的选项中选择最合适的抓取库。为了做出明智的决定,你应该首先在浏览器中探索平台。打开开发者工具,查看GitHub仓库页面所发出的AJAX请求。你会注意到大部分可以忽略。实际上,大多数页面数据都嵌入在服务器返回的HTML文档中。
这意味着一个用于向服务器发送HTTP请求的库与一个HTML解析器结合就足够完成任务。因此,你应该选择:
- Requests:Python生态系统中最流行的HTTP客户端库。它简化了发送HTTP请求和处理相应响应的过程。
- Beautiful Soup:一个全面的HTML和XML解析库。它提供了强大的DOM导航和数据提取API,用于网页抓取。
借助Requests
和Beautiful Soup
,你可以有效地使用Python进行GitHub抓取。让我们深入了解如何实现这一目标!
使用Beautiful Soup构建GitHub仓库抓取器
按照这一步步教程,学习如何用Python抓取GitHub。想跳过整个编码和抓取过程吗?购买GitHub数据集。
步骤1:Python项目设置
在开始之前,请确保你满足以下先决条件:
- 在你的计算机上安装了Python 3+:下载安装程序,执行并按照说明进行操作。
- 选择一个Python IDE:推荐使用安装了Python扩展的Visual Studio Code和PyCharm Community Edition。
现在你已经具备了在Python中设置项目的所有必要条件!
在终端中运行以下命令,创建一个github-scraper文件夹并使用Python虚拟环境初始化它:
mkdir github-scraper
cd github-scraper
python -m venv env
在Windows上,运行以下命令来激活环境:
env\Scripts\activate.ps1
在Linux或macOS上,执行:
./env/bin/activate
然后,在项目文件夹中添加一个scraper.py
文件,其中包含以下行:
print('Hello World!')
现在,你的GitHub抓取器只打印“Hello World!”但它很快就会包含从公共仓库中提取数据的逻辑。
你可以使用以下命令启动脚本:
python scraper.py
如果一切顺利,它应该在终端中打印以下信息:
Hello World!
现在你知道它可以正常工作了,打开你喜欢的Python IDE中的项目文件夹。
太棒了!准备好编写一些Python代码吧。
步骤2:安装抓取库
如前所述,Beautiful Soup和Requests帮助你在GitHub上进行网页抓取。在激活的虚拟环境中,执行以下命令将它们添加到项目的依赖项中:
pip install beautifulsoup4 requests
清空scraper.py
,然后用以下代码行导入这两个包:
import requests
from bs4 import BeautifulSoup
确保你的Python IDE没有报告任何错误。你可能会收到一些未使用的导入警告。忽略它们,因为你即将使用这些抓取库来提取GitHub上的仓库数据!
步骤3:下载目标页面
选择一个你想要从中提取数据的GitHub仓库。在本指南中,你将学习如何抓取luminati-proxy仓库。请记住,任何其他仓库都可以,因为抓取逻辑是相同的。
目标页面在浏览器中的样子如下:
将目标页面的URL存储在一个变量中:
url = 'https://github.com/luminati-io/luminati-proxy'
然后,用requests.get()
下载页面:
page = requests.get(url)
在后台,requests
向该URL发送一个HTTP GET请求,并将服务器生成的响应保存在page
变量中。你应该关注的是它的text
属性。这包含了与目标网页关联的HTML文档。用一个简单的print
指令来验证:
print(page.text)
运行抓取器,你应该在终端中看到:
<!DOCTYPE html>
<html lang="en" data-color-mode="dark" data-light-theme="light" data-dark-theme="dark" data-a11y-animated-images="system" data-a11y-link-underlines="false">
<head>
<meta charset="utf-8">
<link rel="dns-prefetch" href="https://github.githubassets.com">
<link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
<link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
<link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
<link rel="preconnect" href="https://github.githubassets.com" crossorigin>
<link rel="preconnect" href="https://avatars.githubusercontent.com">
<!-- Omitted for brevity... -->
Awesome! Let’s now learn how to parse this
太棒了!现在让我们学习如何解析这些HTML文档。
步骤4:解析HTML文档
要解析上面检索到的HTML文档,将其传递给Beautiful Soup
:
soup = BeautifulSoup(page.text, 'html.parser')
BeautifulSoup()
构造函数接受两个参数:
- 包含HTML内容的字符串:这里存储在
page.text
变量中。 Beautiful Soup
将使用的解析器:“html.parser”是Python内置HTML解析器的名称。
BeautifulSoup()
将解析HTML并返回一个可探索的树结构。详细来说,soup
变量提供了有效的方法来选择DOM树中的元素,例如:
- find():返回与传递的选择策略匹配的第一个HTML元素。
- find_all():返回与输入选择策略匹配的HTML元素列表。
- select_one():返回与传递的CSS选择器匹配的第一个HTML元素。
- select():返回与输入CSS选择器匹配的HTML元素列表。
注意,这些方法也可以在树中的单个节点上调用。除了这些方法外,Beautiful Soup
节点对象还公开了:
- find_next_sibling():返回元素的兄弟节点中与给定CSS选择器匹配的第一个HTML节点。
- find_next_siblings():返回元素的兄弟节点中与传递的CSS选择器匹配的所有HTML节点。
有了这些功能,你已经准备好抓取GitHub了。让我们看看怎么做!
步骤5:熟悉目标页面
在编码之前,还有一个重要步骤要完成。从站点抓取数据是关于选择感兴趣的HTML元素并从中提取数据。定义一个有效的选择策略并不总是容易的,你必须花一些时间来分析你的目标网页的结构。
因此,在浏览器中打开GitHub目标页面,并熟悉它。右键点击并选择“检查”以打开开发者工具:
在HTML代码中深入挖掘,你会注意到该站点没有用唯一的类或属性限定很多元素。因此,通常很难导航到所需元素,你可能需要通过兄弟节点以一种棘手的方式进行操作。
别担心。为GitHub制定有效的选择策略可能并不容易,但也不是不可能。继续在开发者工具中检查页面,直到你感觉准备好抓取它!
步骤6:提取仓库数据
目标是从GitHub仓库中提取有用的数据,如星标、描述、最新提交等。因此,你需要初始化一个Python字典来跟踪这些数据。将以下代码添加到你的代码中:
repo = {}
首先,检查名称元素:
注意它具有一个独特的itemprop="name"
属性。选择它并用以下代码提取其文本内容:
name_html_element = soup.select_one('[itemprop="name"]')name = name_html_element.text.strip()
给定一个Beautiful Soup节点,用get_text()方法来访问其文本内容。
如果你在调试器中检查name_html_element.text
,你会看到:
\nluminati-proxy\n
GitHub文本字段往往包含空格和换行符。用Python的strip()函数来删除它们。
在仓库名称下方,有一个分支选择器:
注意没有简单的方法来选择存储主分支名称的HTML元素。你可以选择.octicon-git-branch
节点,然后在其兄弟节点中查找目标span
:
git_branch_icon_html_element = soup.select_one('.octicon-git-branch')
main_branch_html_element = git_branch_icon_html_element.find_next_sibling('span')
main_branch = main_branch_html_element.get_text().strip()
通过图标到达感兴趣的元素的模式在GitHub上非常有效。在本节中,你会看到它被多次使用。
现在,关注分支头:
用以下代码提取最新提交时间:
relative_time_html_element = boxheader_html_element.select_one('relative-time')
latest_commit = relative_time_html_element['datetime']
给定一个节点,你可以像在Python字典中一样访问其HTML属性。
本节中的另一个重要信息是提交数量:
用前面描述的图标模式收集它:
history_icon_html_element = boxheader_html_element.select_one('.octicon-history')
commits_span_html_element = history_icon_html_element.find_next_sibling('span')
commits_html_element = commits_span_html_element.select_one('strong')
commits = commits_html_element.get_text().strip().replace(',', '')
注意find_next_sibling()
只能访问顶级兄弟节点。要选择它们的子节点之一,你必须首先获取兄弟元素,然后调用select_one()
,如上所示。
由于GitHub上超过一千的数字包含逗号,用Python的replace()
方法删除它。
接下来,关注右侧的信息框:
用以下代码选择它:
bordergrid_html_element = soup.select_one('.BorderGrid')
检查描述元素:
再次通过兄弟节点选择它:
about_html_element = bordergrid_html_element.select_one('h2')
description_html_element = about_html_element.find_next_sibling('p')
description = description_html_element.get_text().strip()
然后,应用图标模式来检索仓库的星标、观察者和分叉数量。
关注图标,然后是它们的文本兄弟节点:
star_icon_html_element = bordergrid_html_element.select_one('.octicon-star')
stars_html_element = star_icon_html_element.find_next_sibling('strong')
stars = stars_html_element.get_text().strip().replace(',', '')
eye_icon_html_element = bordergrid_html_element.select_one('.octicon-eye')
watchers_html_element = eye_icon_html_element.find_next_sibling('strong')
watchers = watchers_html_element.get_text().strip().replace(',', '')
fork_icon_html_element = bordergrid_html_element.select_one('.octicon-repo-forked')
forks_html_element = fork_icon_html_element.find_next_sibling('strong')
forks = forks_html_element.get_text().strip().replace(',', '')
干得好!你刚刚抓取了一个GitHub仓库。
步骤7:抓取readme
另一个需要提取的重要信息是README.md
文件。这是一个可选的文本文件,描述了GitHub仓库并解释了如何使用代码。
如果你点击README.md
文件,然后点击“原始”按钮,你将被重定向到以下URL:
https://raw.githubusercontent.com/luminati-io/luminati-proxy/master/README.md
因此可以推断,GitHub仓库的readme文件的URL遵循以下格式:
https://raw.githubusercontent.com/<repo_id>/<repo_main_branch>/README.md
由于你在main_branch
变量中存储了<repo_main_branch>
信息,你可以用Python的f
字符串来编程构建这个URL:
readme_url = f'https://raw.githubusercontent.com/luminati-io/luminati-proxy/{main_branch}/README.md'
然后,用requests
来检索readme的原始Markdown内容:
readme_url = f'https://raw.githubusercontent.com/luminati-io/luminati-proxy/{main_branch}/README.md'
readme_page = requests.get(readme_url)
readme = None
# if there is a README.md file
if readme_page.status_code != 404:
readme = readme_page.text
注意404检查,以避免在仓库没有readme文件时存储GitHub的404页面内容。
步骤8:存储抓取的数据
不要忘记将抓取的数据变量添加到repo
字典中:
repo['name'] = name
repo['latest_commit'] = latest_commit
repo['commits'] = commits
repo['main_branch'] = main_branch
repo['description'] = description
repo['stars'] = stars
repo['watchers'] = watchers
repo['forks'] = forks
repo['readme'] = readme
用print(repo)
来确保数据提取过程按预期工作。运行Python GitHub抓取器,你将得到:
{'name': 'luminati-proxy', 'latest_commit': '2023-08-09T08:25:15Z', 'commits': '1079', 'main_branch': 'master', 'description': 'Luminati HTTP/HTTPS Proxy manager', 'stars': '645', 'watchers': '55', 'forks': '196', 'readme': '# Proxy manager\n\n (omitted for brevity...)'}
太棒了!你知道如何抓取GitHub了!
步骤9:将抓取的数据导出为JSON
最后一步是使收集的数据更容易分享、阅读和分析。最好的方法是将数据导出为一种人类可读的格式,如JSON:
import json
# ...
with open('repo.json', 'w') as file:
json.dump(repo, file, indent=4)
从Python标准库中导入json
,用open()
初始化一个repo.json
文件,最后用json.dump()
填充它。查看我们的指南,了解更多关于如何在Python中解析JSON的内容。
完美!是时候看看整个GitHub Python抓取器了。
步骤10:把所有东西放在一起
这是完整的scraper.py
文件的样子:
import requests
from bs4 import BeautifulSoup
import json
# the URL of the target repo to scrape
url = 'https://github.com/luminati-io/luminati-proxy'
# download the target page
page = requests.get(url)
# parse the HTML document returned by the server
soup = BeautifulSoup(page.text, 'html.parser')
# initialize the object that will contain
# the scraped data
repo = {}
# repo scraping logic
name_html_element = soup.select_one('[itemprop="name"]')
name = name_html_element.get_text().strip()
git_branch_icon_html_element = soup.select_one('.octicon-git-branch')
main_branch_html_element = git_branch_icon_html_element.find_next_sibling('span')
main_branch = main_branch_html_element.get_text().strip()
# scrape the repo history data
boxheader_html_element = soup.select_one('.Box .Box-header')
relative_time_html_element = boxheader_html_element.select_one('relative-time')
latest_commit = relative_time_html_element['datetime']
history_icon_html_element = boxheader_html_element.select_one('.octicon-history')
commits_span_html_element = history_icon_html_element.find_next_sibling('span')
commits_html_element = commits_span_html_element.select_one('strong')
commits = commits_html_element.get_text().strip().replace(',', '')
# scrape the repo details in the right box
bordergrid_html_element = soup.select_one('.BorderGrid')
about_html_element = bordergrid_html_element.select_one('h2')
description_html_element = about_html_element.find_next_sibling('p')
description = description_html_element.get_text().strip()
star_icon_html_element = bordergrid_html_element.select_one('.octicon-star')
stars_html_element = star_icon_html_element.find_next_sibling('strong')
stars = stars_html_element.get_text().strip().replace(',', '')
eye_icon_html_element = bordergrid_html_element.select_one('.octicon-eye')
watchers_html_element = eye_icon_html_element.find_next_sibling('strong')
watchers = watchers_html_element.get_text().strip().replace(',', '')
fork_icon_html_element = bordergrid_html_element.select_one('.octicon-repo-forked')
forks_html_element = fork_icon_html_element.find_next_sibling('strong')
forks = forks_html_element.get_text().strip().replace(',', '')
# build the URL for README.md and download it
readme_url = f'https://raw.githubusercontent.com/luminati-io/luminati-proxy/{main_branch}/README.md'
readme_page = requests.get(readme_url)
readme = None
# if there is a README.md file
if readme_page.status_code != 404:
readme = readme_page.text
# store the scraped data
repo['name'] = name
repo['latest_commit'] = latest_commit
repo['commits'] = commits
repo['main_branch'] = main_branch
repo['description'] = description
repo['stars'] = stars
repo['watchers'] = watchers
repo['forks'] = forks
repo['readme'] = readme
# export the scraped data to a repo.json output file
with open('repo.json', 'w') as file:
json.dump(repo, file, indent=4)
在不到100行代码中,你可以构建一个网络蜘蛛来收集仓库数据。
用以下命令运行脚本:
python scraper.py
等待抓取过程完成,你将在项目的根文件夹中找到一个repo.json
文件。打开它,你会看到:
{
"name": "luminati-proxy",
"latest_commit": "2023-08-09T08:25:15Z",
"commits": "1079",
"main_branch": "master",
"description": "Luminati HTTP/HTTPS Proxy manager",
"stars": "645",
"watchers": "55",
"forks": "196",
"readme": "# Proxy manager\n\n[![dependencies Status](https://david-dm.org/luminati-io/luminati-proxy/status.svg)](https://david-dm.org/luminati-io/luminati-proxy)\n[![devDependencies Status](https://david-dm.org/luminati-io/luminati-proxy/dev-status.svg)](https://david-dm..."
}
恭喜你!你从网页中的原始数据开始,现在在JSON文件中拥有半结构化数据。你刚刚学会了如何用Python构建一个GitHub仓库抓取器!
结论
在这份逐步指南中,你理解了构建GitHub仓库抓取器的原因。具体来说,你在一个指导性的教程中学习了如何抓取GitHub。正如这里所示,这只需要几行代码。
同时,越来越多的网站采用了反抓取技术。这些技术可以通过IP禁封和速率限制来识别和阻止请求,防止你的抓取器访问网站。避免这些技术的最好方法是代理。探索Bright Data的丰富顶级代理服务和专用GitHub代理。
Bright Data控制着市场上最大的、最可靠的抓取导向的代理网络之一,为财富500强公司和超过2万名客户提供服务。其全球代理网络包括:
总的来说,Bright Data是市场上最大的、最可靠的抓取导向的代理网络之一。与我们的销售代表联系,看看Bright Data的哪种产品最适合你的需求。