发布于 2026-01-06 16 阅读
0

Python 网络爬虫入门指南:初步研究、构建爬虫程序、整理网络爬虫数据、洞察分析、应用案例

Python网页爬虫入门教程

第一步

初步研究

搭建爬虫

整理你的网络抓取数据

洞察

应用程序

第一步

网络爬虫是指从网页源代码中提取数据,而不是通过网页所有者提供的API接口。虽然一开始可能有点棘手,但它能让你轻松地从网络上抓取和整理大量信息,而无需手动复制粘贴

今天为了进行一些基本的网页抓取,我将使用 Python 库BeautifulSoup。如果您之前没有使用过这个包,则需要先安装它。最简单的方法是使用 Python 包管理器。首先,通过尝试使用包管理器安装库来pip检查您的计算机上是否已安装了该库:pip

$ pip install beautifulsoup4
Enter fullscreen mode Exit fullscreen mode

如果您已安装 Python 但尚未安装pip(如果上述步骤报错),pip请按照此处的说明单独安装。macOS 和大多数 Linux 发行版默认都预装了 Python,但如果您使用的是 Windows 系统且需要安装 Python,请尝试访问其官方网站

Python 2.7 已于 2020 年 1 月 1 日弃用,所以最好直接安装 Python 3(如果您还没有安装的话)。我还没有安装 Python 3(因为不久前我刚恢复了 Mac 的出厂设置),所以我先按照这些说明进行安装,这些说明其实可以归结为:

$ brew install python
Enter fullscreen mode Exit fullscreen mode

现在,我们可以检查 Python 2 和 Python 3 是否都已安装,并且pipPython 3 是与 Python 2 一起安装的:

$ python --version
Python 2.7.10

$ python3 --version
Python 3.7.2

$ pip --version
-bash: pip: command not found

$ pip3 --version
pip 19.0.2 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
Enter fullscreen mode Exit fullscreen mode

最后,让我们开始BeautifulSoup使用吧pip3

$ pip3 install beautifulsoup4
Enter fullscreen mode Exit fullscreen mode

请注意,此时您可以使用“普通”的 Python 解释器执行该python3命令,或者您可以通过安装 IPython 来使用功能更丰富的IPython

$ pip3 install ipython
Enter fullscreen mode Exit fullscreen mode

在本教程中,我将使用 IPython。

初步研究

我发起这个项目的初衷是想根据Indeed和类似网站上的招聘信息,创建一个特定领域、特定级别开发人员的“平均画像”。虽然这需要一些技巧,可能还要用到正则表达式,但一个好的起点是简单地查看某项技术在招聘信息中出现的频率:提及次数越多=越重要,对吧?

BeautifulSoupid它允许您按类型、标签等访问页面的 XML/HTML 标签。例如,class您可以提取所有标签,或者获取具有特定标签的所有标签的文本。因此,要以常规方式提取数据,我们需要剖析要抓取的页面的结构。让我们首先在纽约市搜索 JavaScript 开发人员:<a><p>class

请记下此网页的网址:

https://www.indeed.com/jobs?q=javascript+developer&l=New+York+City

如果翻到搜索结果的第二页,内容会变成:

https://www.indeed.com/jobs?q=javascript+developer&l=New+York+City&start=10

……以及搜索结果的第三页:

https://www.indeed.com/jobs?q=javascript+developer&l=New+York+City&start=20

好的,所以每页显示 10 条结果,从第一页开始,每页的 URL 中都会多出一个参数:&start=...,其中...是 10 的正倍数。(实际上,我们可以&start=0在第一页的 URL 后加上 ,返回的结果也一样。)好的,现在我们知道如何访问多页结果了……接下来呢?我们来看看第一页结果的结构:

我注意到,每个招聘广告的链接似乎都有一个onmousedown变化规律的标签。第一个标签是……

onmousedown="return rclk(this,jobmap[0],0);"
Enter fullscreen mode Exit fullscreen mode

……第二个是

onmousedown="return rclk(this,jobmap[1],0);"
Enter fullscreen mode Exit fullscreen mode

以此类推。我敢肯定,我们可以提取所有包含“ ”的<a>标签,这样就能找到此页面上所有职位的链接。我们先把这个方法记下来,打开其中一个广告——看看能不能在这些页面中找到职位说明:onmousedownreturn rclk(this,jobmap[

广告正文似乎包含在一个<div>带有标签的子页面中class="jobsearch-JobComponent-description"。这听起来像是一个非常具体的子页面div。我假设每个页面上的子页面都一样,但你可以自己检查一下。现在我们知道了要访问的URL结构,知道如何在这些页面上找到职位广告的链接,以及广告文本在这些子页面中的位置,我们可以编写一个网页抓取脚本了!

搭建爬虫

我们先从遍历搜索页面开始。我们的 URL 看起来会像这样:

https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=
Enter fullscreen mode Exit fullscreen mode

但是我们需要在末尾添加一个非负的 10 的倍数。在 Python 中,一个简单的方法是创建一个range循环:

In [91]: for pageno in range(0,10): 
    ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
    ...:     print(search) 
    ...:                                                                                                                                           
https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=0
https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=10
https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=20
...
https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=90
Enter fullscreen mode Exit fullscreen mode

看起来不错!请注意,我们需要使用 Python 的方法将整数转换为字符串str()

我们真正想要做的是访问这些页面并提取它们的内容。我们可以使用 Python 的 ` urllibparse` 模块来实现这一点——具体来说urllib.request.urlopen()仅限 Python 3BeautifulSoup )。然后,我们只需调用构造函数即可解析页面BeautifulSoup。为了测试这一点,让我们暂时将循环范围缩小到仅一个页面,并使用以下代码打印页面内容soup.prettify()

In [100]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:     print(soup.prettify()[:500]) 
     ...:                                                                                                                                          
<!DOCTYPE html>
<html dir="ltr" lang="en">
 <head>
  <meta content="text/html;charset=utf-8" http-equiv="content-type"/>
  <script src="/s/a3599cf/en_US.js" type="text/javascript">
  </script>
  <link href="/s/97464e7/jobsearch_all.css" rel="stylesheet" type="text/css"/>
  <link href="http://rss.indeed.com/rss?q=javascript&amp;l=New+York+City" rel="alternate" title="Javascript Jobs, Employment in New York, NY" type="application/rss+xml"/>
  <link href="/m/jobs?q=javascript&amp;l=New+York+City" m
Enter fullscreen mode Exit fullscreen mode

我使用字符串切片对输出进行了截断,将其限制为 500 个字符(此页面的源代码很长)。不过,即使在这短短的片段中,您也能看到我们最初的搜索内容:q=javascript&amp;l=New+York+City

太好了!看来这个方法有效。select()现在我们用它来抓取此页面上的所有招聘广告链接。记住,我们要查找所有包含“ ”的<a>标签。我们需要使用特殊的语法来实现这个目标,如下所示:onmousedownreturn rclk(this,jobmap[

In [102]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="return rclk(this,jobmap["]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         print(subURL) 
     ...:                                                                                                                                          
https://www.indeed.com/rc/clk?jk=43837af9ab727a8b&fccid=927356efef1f3075&vjs=3
https://www.indeed.com/rc/clk?jk=6511fae8b53360f1&fccid=f057e04c37cca134&vjs=3
https://www.indeed.com/company/Transport-Learning/jobs/React-HTML-Javascript-Developer-ca898e4825aa3f36?fccid=6b6d25caa00a7d0a&vjs=3
...
https://www.indeed.com/rc/clk?jk=9a3a9b4a4cbb3f28&fccid=101a2d7616184cc8&vjs=3
Enter fullscreen mode Exit fullscreen mode

我们在每个链接的开头都加上“ https://www.indeed.com ”,因为在页面源代码中,所有链接href都是相对路径。如果我们复制其中一个链接(比如第三个),并将其粘贴到浏览器中,应该就能看到招聘广告:

看起来不错!好的,接下来呢?嗯,我们想再次打开这些子页面BeautifulSoup并解析源代码。但这次,我们要查找<div>包含class特定内容的页面jobsearch-JobComponent-description。所以,让我们再次使用字符串切片,并打印每个页面的前50个字符,以确保所有这些URL都能正常工作:

In [103]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:         print(subSOUP.prettify()[:50]) 
     ...:                                                                                                                                          
<html dir="ltr" lang="en">
 <head>
  <title>
   Ne
<html dir="ltr" lang="en">
 <head>
  <title>
   Re
<html dir="ltr" lang="en">
 <head>
  <title>
   Re
...
<html dir="ltr" lang="en">
 <head>
  <title>
   Ni
Enter fullscreen mode Exit fullscreen mode

太好了!目前一切正常。下一步是尝试提取每个广告正文的内容。我们使用之前查找子页面中具有包含以下属性的元素*=时使用的相同语法select()<div>classjobsearch-JobComponent-description

In [106]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:          
     ...:         for desc in subSOUP.select('div[class*="jobsearch-JobComponent-description"]'): 
     ...:             print(desc.get_text()[:50]) 
     ...:                                                                                                                                          
Impact

Ever wondered how Amazon offers the Earth'
Mobile & Web Engineering is looking for talented w
Job Description

We are looking for a talented Fro
$75,000 - $95,000 a yearYour first few months:We c
Michael Kors is always interested in hearing from 
Facebook's mission is to give people the power to 
$70,000 - $80,000 a yearWe Make Websites are the g
InternshipApplications are due by June 27, 2019 at
Job Overview:

UI Developer should have a very goo
* THIS IS A REMOTE POSITION *

At Dental Intellige
Enter fullscreen mode Exit fullscreen mode

BeautifulSoup.select()返回与我们提供的搜索参数匹配的 HTML/XML 标签。我们可以使用方括号表示法(例如 `<a>` adlink['href'])从这些标签中提取属性,也可以使用 `<a>` 提取开始标签和结束标签(例如 `<a>`<p>和 ` <b>` 之间</p>)中包含的文本get_text(),就像我们上面所做的那样。该subSOUP.select()语句返回一个标签列表<div>,其中包含class包含子字符串“" jobsearch-JobComponent-description"”的属性,然后我们使用for ... in循环遍历该列表(只有一个标签),并使用`<a>` 打印标签<div>中包含的文本<div></div>get_text()

结果就是一串乱码文本。由于每条描述只保留了50个字符,所以看起来毫无意义。但现在我们已经拥有了一个功能齐全的Indeed招聘广告抓取工具!我们只需要弄清楚如何处理这些结果来完成任务。

整理你的网络抓取数据

最简单的办法是列出我们感兴趣的关键词。我们来看看各种 JavaScript 框架的流行程度。比如:

frameworks = ['angular', 'react', 'vue', 'ember', 'meteor', 'mithril', 'node', 'polymer', 'aurelia', 'backbone']
Enter fullscreen mode Exit fullscreen mode

……这或许是个不错的开始。如果您熟悉处理此类文本数据,就会知道我们需要将所有内容转换为小写,以避免“React”和“react”之间的歧义;我们需要移除标点符号,以免将“Angular”和“Angular,”视为两个不同的事物;我们还可以使用空格轻松地将文本拆分为词元split()。首先,让我们拆分每个广告的文本,将每个单词转换为小写,看看我们的单词列表是什么样的:

In [110]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:          
     ...:         for desc in subSOUP.select('div[class*="jobsearch-JobComponent-description"]'): 
     ...:             words = desc.get_text().lower().split()[:50] 
     ...:             for word in words: 
     ...:                 print(word) 
     ...:                                                                                                                                          
mobile
&
web
engineering
is
looking
for
talented
web
developers
to
join
the
digital
acquisitions
engineering
group.
...
Enter fullscreen mode Exit fullscreen mode

等等。我们来挑一些比较奇怪的例子:

group.
role,
summary:
recoded:you'd
limitless.we
react.within
Enter fullscreen mode Exit fullscreen mode

……好的,所以我们需要按空格以及逗号.,逗号和逗号进行分割:。列表的其他部分还有:

2.0-enabled
Enter fullscreen mode Exit fullscreen mode

当然,拆分会对其造成损害.,但我认为这样做的好处大于坏处。我们还有很多带连字符的单词,例如

blue-chip
data-driven,
hyper-personalized,
go-to
team-based
e-commerce
Enter fullscreen mode Exit fullscreen mode

所以,我们可能不应该用连字符或破折号来分割字符。不过,我们确实有一两个这样的例子。

trends/development
qa/qc
Enter fullscreen mode Exit fullscreen mode

所以我们也需要进行拆分/。最后,对于诸如此类的拼写错误,我们无能为力:

analystabout
part-timeat
contractlocation:
yearyour
Enter fullscreen mode Exit fullscreen mode

目前还不行,所以我们只能先这样。为了让这个方案更健壮一些,我们希望能够使用多个分隔符进行分割,而不仅仅是空格字符。因此,我们需要 Python 的正则表达式库re

In [110]: import re

In [111]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:          
     ...:         for desc in subSOUP.select('div[class*="jobsearch-JobComponent-description"]'): 
     ...:             words = re.split("[ ,.:/]", desc.get_text().lower())[:50] 
     ...:             for word in words: 
     ...:                 print(word) 
     ...:                                                                                                                                          
impact

ever
wondered
how
amazon
offers
the
earth's
biggest
selection
and
still
...
Enter fullscreen mode Exit fullscreen mode

好的。那么,我们现在都有哪些怪人呢?

earth's

customers?
$75
000
-
$95
000
(both
ios
and
android)
facebook's
$70
000
-
$80
000
11
59pm
*
Enter fullscreen mode Exit fullscreen mode

所以,还有一些特殊情况需要处理。容易解决的问题包括:移除's单词末尾的空格,以及在分隔符列表中添加逗号?、逗号和逗号(以及空格逗号和逗号等空白字符)。(再快速检查一下,显然我们也应该在分隔符列表中添加逗号。)我们还可以忽略长度为一个字符或更少的单词。处理时间(例如 11:59pm)和薪资(例如 70,000 美元 - 80,000 美元)的问题比较复杂,这里就不赘述了。目前,我们先忽略这些问题。现在,让我们来看看改进后的爬虫:()\n\t\r!

In [121]: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:          
     ...:         for desc in subSOUP.select('div[class*="jobsearch-JobComponent-description"]'): 
     ...:             words = re.split("[ ,.:/?!()\n\t\r]", desc.get_text().lower())[:50] 
     ...:             for word in words: 
     ...:                 word = word.strip() 
     ...:                 if word.endswith("'s"): 
     ...:                     word = word[:-2] 
     ...:                 if len(word) < 2: 
     ...:                     continue 
     ...:                 print(word) 
     ...:                       
Enter fullscreen mode Exit fullscreen mode

太漂亮了!那么,我们该拿它做什么呢?

洞察

与其简单地打印单词列表,不如将它们添加到字典中。每次遇到一个新单词,我们都可以将其添加到字典中,初始值为 1;每次遇到一个之前出现过的单词,我们都可以将其计数器加一:

In [123]: counts = {} 
     ...:  
     ...: for pageno in range(0,1): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(10*pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         subURL  = "https://www.indeed.com" + adlink['href'] 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:         print("Scraping: " + subURL + "...") 
     ...:          
     ...:         for desc in subSOUP.select('div[class*="jobsearch-JobComponent-description"]'): 
     ...:             words = re.split("[ ,.:/?()\n\t\r]", desc.get_text().lower())[:50] 
     ...:             for word in words: 
     ...:                 word = word.strip() 
     ...:                 if word.endswith("'s"): 
     ...:                     word = word[:-2] 
     ...:                 if len(word) < 2: 
     ...:                     continue 
     ...:                 if word in counts: 
     ...:                     counts[word] += 1 
     ...:                 else: 
     ...:                     counts[word] = 1 
     ...:  
     ...: print(counts) 
     ...:                                                                                                                                          
Scraping: https://www.indeed.com/company/CypressG/jobs/Newer-Javascript-Framework-Developer-5a17b0475e76de26?fccid=dc16349e968c035d&vjs=3...
Scraping: https://www.indeed.com/company/Transport-Learning/jobs/React-HTML-Javascript-Developer-ca898e4825aa3f36?fccid=6b6d25caa00a7d0a&vjs=3...
Scraping: https://www.indeed.com/rc/clk?jk=a0727d28799f1dff&fccid=5d5fde8e5925b19a&vjs=3...
...
Scraping: https://www.indeed.com/rc/clk?jk=b084048e6a1b2727&fccid=5d5fde8e5925b19a&vjs=3...
{'$80': 1, '000': 8, '$250': 1, 'yeari': 1,...
Enter fullscreen mode Exit fullscreen mode

我添加了“正在抓取”的提示信息,以便我们确认脚本正在运行。请注意,生成的字典是无序的!如果我们想按值排序,有几种方法可以做到,但最简单的方法可能是将其转换为元组列表,交换键和值,这样我们就可以轻松地按键(特定单词出现的次数)排序:

word_freq = [] 

for key, value in counts.items(): 
word_freq.append((value,key)) 

word_freq.sort(reverse=True)
Enter fullscreen mode Exit fullscreen mode

我们按reverse=True从高到低的顺序排序,最常用的词排在列表顶部。让我们看看结果:

[(19, 'to'), (13, 'and'), (12, 'the'), (11, 'for'), (9, 'of'), (9, 'is'), (6, 'we'), (6, 'in'), (6, '000'), (5, 'you')]
Enter fullscreen mode Exit fullscreen mode

当然,我们之所以要筛选出特定的词(例如“angular”、“react”等等),是因为如果不这样做,我们会得到一大堆无用的填充词(例如“to”、“and”等等)。让我们定义一个“有效”词列表,word对照列表检查结果,只统计我们关心的词。最后,我还会去掉[:50]用于调试的切片,并将搜索范围扩大到前100页结果。以下是最终脚本:

In [127]: counts = {} 
     ...: frameworks = ['angular', 'react', 'vue', 'ember', 'meteor', 'mithril', 'node', 'polymer', 'aurelia', 'backbone'] 
     ...: max_pages = 100 
     ...: ads_per_page = 10 
     ...: max_ads = max_pages * ads_per_page 
     ...:  
     ...: for pageno in range(0, max_pages): 
     ...:     search = "https://www.indeed.com/jobs?q=javascript&l=New+York+City&start=" + str(ads_per_page * pageno) 
     ...:     url = urllib.request.urlopen(search) 
     ...:     soup = BeautifulSoup(url) 
     ...:     this_page_ad_counter = 0 
     ...:      
     ...:     for adlink in soup.select('a[onmousedown*="rclk(this,jobmap"]'): 
     ...:         href = adlink['href'] 
     ...:         subURL  = "https://www.indeed.com" + href 
     ...:         subSOUP = BeautifulSoup(urllib.request.urlopen(subURL)) 
     ...:         ad_index = this_page_ad_counter + pageno*ads_per_page 
     ...:         print("Scraping (" + str(ad_index + 1) + "/" + str(max_ads) + "): " + href + "...") 
     ...:         this_page_ad_counter += 1 
     ...:          
     ...:         for desc in subSOUP.select('div[class*="jobsearch-JobComponent-description"]'): 
     ...:             words = re.split("[ ,.:/?()\n\t\r]", desc.get_text().lower()) 
     ...:             for word in words: 
     ...:                 word = word.strip() 
     ...:                 if word.endswith("'s"): 
     ...:                     word = word[:-2] 
     ...:                 if word.endswith(".js"): 
     ...:                     word = word[:-3] 
     ...:                 if word.endswith("js"): 
     ...:                     word = word[:-2] 
     ...:                 if len(word) < 2: 
     ...:                     continue 
     ...:                 if word not in frameworks: 
     ...:                     continue 
     ...:                 if word in counts: 
     ...:                     counts[word] += 1 
     ...:                 else: 
     ...:                     counts[word] = 1 
     ...:  
     ...: word_freq = []  
     ...:   
     ...: for key, value in counts.items():  
     ...:     word_freq.append((value,key))  
     ...:       
     ...: word_freq.sort(reverse=True) 
     ...:  
     ...: print(word_freq) 
     ...:                                                                                                                                          
Scraping (1/1000): /rc/clk?jk=72b4ac2da9ecb39d&fccid=f057e04c37cca134&vjs=3...
Scraping (2/1000): /company/Transport-Learning/jobs/React-HTML-Javascript-Developer-ca898e4825aa3f36?fccid=6b6d25caa00a7d0a&vjs=3...
Scraping (3/1000): /rc/clk?jk=9a3a9b4a4cbb3f28&fccid=101a2d7616184cc8&vjs=3...
...
Enter fullscreen mode Exit fullscreen mode

我做了一些小的美化改动……你能看出改动在哪里吗?我还确保去掉了所有框架名称末尾的“.js”或“js”,这样它们就不会被当作单独的项目来处理。我从脚本中移除了“魔法数字”10,并将其放入一个描述性变量中ads_per_page。此外,我还创建了一个变量,max_pages用于设置我只查看100页搜索结果,所以总共我会查看Indeed上纽约地区最近发布的1000条“Javascript”广告。

这需要一些时间,所以我先去喝杯咖啡,一会儿再回来……


那么,结果如何呢?

[(556, 'react'), (313, 'angular'), (272, 'node'), (105, 'vue'), (45, 'backbone'), (36, 'ember'), (4, 'polymer')]
Enter fullscreen mode Exit fullscreen mode

因此,在抓取的 1000 条广告中,有 556 条提到了“react”,313 条提到了“angular”,以此类推。一个简单的脚本就能得出这么多有用的信息!

应用程序

稍加完善,这可以发展成一个网站/应用程序,开发者(或任何求职者)可以通过它了解平均职位要求(“……56%的招聘广告要求具备React经验……”)、平均薪资(“……55,000美元±2,000美元……”),并与这些平均值进行对比。这样的工具在薪资谈判中非常有用,或者在决定学习哪些新技术/语言以提升职业发展时也很有帮助。可以通过跟踪招聘广告发布日期并剔除过时信息(例如超过一周的信息)来保持数据的时效性。

这些信息对雇主也很有用,能让他们更好地了解如何根据职位、经验水平等因素设定薪资。Indeed 只是第一步,但这种数据抓取方法很容易扩展到多个招聘网站。

这个原型只花了我几个小时的时间,而且我Python经验也有限。我想一个小团队应该能在几周内把这个应用开发出来并运行。大家觉得呢?有没有人知道类似的项目?

文章来源:https://dev.to/awwsmm/web-scraping-walkthrough-with-python-85c