Python程序员需要掌握的网络爬虫技术

干货教程部分 :

作者:黄永祥(也是书的作者噢)

当下是一个大数据的时代,各个行业都离不开数据的支持,因此,网络爬虫应运而生。编写网络爬虫当下最为火热的语言毫无疑问是Python,原因是,Python开发爬虫相对简单,功能库完善,易于学习。

本教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握的爬虫编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12键打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请求参数、请求方式和响应内容。如图所示。

从图上可以看到,我们将搜索关键字添加设置python,搜索地区设为广州。浏览器的地址为:

https://search.51job.com/list/030200,000000,0000,00,9,99,python,2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare=

由请求链接的格式分析可知,该请求为GET请求,并且带有多个请求参数。为了简化请求参数,我们在浏览器上依次删除请求参数并访问删除后的请求链接,对比删除之前与删除之后的网页变化。最后请求链接的优化如下:

https://search.51job.com/list/030200,000000,0000,00,9,99,python,2,1.html

从优化后的请求链接可以看到,搜索关键字和搜索区域是隐藏在请求链接的某个位置中。搜索关键字的位置是直观可以看到的,而搜索区域需要进行分析。我们将区域改为上海,其请求链接如下:

https://search.51job.com/list/020000,000000,0000,00,9,99,python,2,1.html

通过对比发现,搜索区域030200代表广州,020000代表上海。那么问题来了,如果要切换到其他城市,怎样获取该城市的编号? 针对这个问题,首先从上述的编号分析其特性,发现编号不存在规律性,那么应该是由网站自行定义的。因此可以在浏览器的开发者工具下查找相关的请求信息,最后在js下查到以下信息,如图所示。

现在确定了搜索关键字和搜索区域后,我们还需要确定页数,因为搜索出来的结果肯定是进行分页处理。以搜索关键字为python,搜索地区为广州,点击第二页,其请求链接如下:

# 第一页
https://search.51job.com/list/030200,000000,0000,00,9,99,python,2,1.html
# 第二页
https://search.51job.com/list/030200,000000,0000,00,9,99,python,2,2.html

对比发现,我们可确定页数的位置,最终,请求链接的地址修改如下:

# 变量cityCode是城市编号
# 变量keyWord是搜索关键词
# 变量pageNum是搜索页数
'https://search.51job.com/list/'+cityCode+',000000,0000,00,9,99,'+keyWord+',2,'+pageNum+'.html'

确定请求链接后,我们在分析该请求的响应内容,从响应内容中获取所需的数据内容,如图所示。

从图上可知,我们需要爬取职位的岗位要求和任职要求的数据,因此在当前页面中,我们需要获取职位的URL地址。根据上述分析,功能代码如下:

import requests
from bs4 import BeautifulSoup
# 函数参数分别为城市编号、关键词和循环的页数
def get_url(http://mmbiz.qpic.cn/mmbiz/G1lssUsxJOsVVJNUIuKfUP7bLm5EVWxXl5znicMum6Os0CMJHPdeHicicZ4W5MGOVa8ooSXYuE61Ek/0):
    headers = {
            'Host':'search.51job.com',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
        }
    temp_list = []

    for i in range(int(pageNum)):
        url = 'https://search.51job.com/list/'+cityCode+',000000,0000,00,9,99,'+keyWord+',2,'+str(i+1)+'.html'
        r = requests.get(url, headers=headers)
        soup = BeautifulSoup(r.content.decode('gbk'),'html5lib')
        find_div = soup.find_all('div',class_='el')
        # 获取职位的URL
        for j in find_div:
            find_href = j.find('a')
            if 'https://jobs.51job.com' in str(find_href):
                temp_list.append(find_href['href'])
    return temp_list

得到职位的URL之后,接下来分析职位的信息页面。在职位信息页,分别获取职位信息和任职要求,网页分析如图所示。

图上的响应内容中,职位信息是在HTML的div标签,属性class为bmsg job_msg inbox,并且属性值是唯一的,因此可以通过该标签进行定位获取数据。其代码功能如下:

# 获取职位信息
def get_data(job_url):
    headers = {
        'Host': 'jobs.51job.com',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
    }

    # 遍历职位url列表,获取每个职位的职位信息
    for url in job_url:
        r = requests.get(url, headers=headers)
        soup = BeautifulSoup(r.content.decode('gbk'), 'html5lib')
        find_job = soup.find('div', class_='bmsg job_msg inbox').find_all('p')
        temp_list = []
        # 获取职位信息
        for k in find_job:
            # 简单的数据清洗
            if not ':' in str(k) and not ':' in str(k) and k.getText():
                if '、' in k.getText():
                    text = k.getText().split('、')[1].strip()
                else:
                    text = k.getText().strip()
                temp_list.append(text)
        # 将数据写入CSV文件
        if ''.join(temp_list).strip():
            f = open('text.csv', 'a', newline='', encoding='utf-8')
            writer = csv.writer(f)
            writer.writerow([''.join(temp_list)])
            f.close()

我们将两个函数get_url和get_data写在spider.py文件,代码如下:
import requests
from bs4 import BeautifulSoup
import csv
# 函数参数分别为城市编号、关键词和循环的页数
def get_url(http://mmbiz.qpic.cn/mmbiz/G1lssUsxJOsVVJNUIuKfUP7bLm5EVWxXl5znicMum6Os0CMJHPdeHicicZ4W5MGOVa8ooSXYuE61Ek/0):
    headers = {
            'Host':'search.51job.com',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
        }
    temp_list = []
    for i in range(int(pageNum)):
        url = 'https://search.51job.com/list/'+cityCode+',000000,0000,00,9,99,'+keyWord+',2,'+str(i+1)+'.html'
        r = requests.get(url, headers=headers)
        soup = BeautifulSoup(r.content.decode('gbk'),'html5lib')
        find_div = soup.find_all('div',class_='el')
        # 获取职位的URL
        for j in find_div:
            find_href = j.find('a')
            if 'https://jobs.51job.com' in str(find_href):
                temp_list.append(find_href['href'])
    return temp_list

# 获取职位信息
def get_data(job_url):
    headers = {
        'Host': 'jobs.51job.com',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
    }
    # 遍历职位url列表,获取每个职位的职位信息
    for url in job_url:
        r = requests.get(url, headers=headers)
        soup = BeautifulSoup(r.content.decode('gbk'), 'html5lib')
        find_job = soup.find('div', class_='bmsg job_msg inbox').find_all('p')
        temp_list = []
        # 获取职位信息
        for k in find_job:
            # 简单的数据清洗
            if not ':' in str(k) and not ':' in str(k) and k.getText():
                if '、' in k.getText():
                    text = k.getText().split('、')[1].strip()
                else:
                    text = k.getText().strip()
                temp_list.append(text)
        # 将数据写入CSV文件
        if ''.join(temp_list).strip():
            f = open('text.csv', 'a', newline='', encoding='utf-8')
            writer = csv.writer(f)
            writer.writerow([''.join(temp_list)])
            f.close()

if __name__ == '__main__':
    cityCode = '030200'
    keyWord = 'python'
    pageNum = 2
    job_url = get_url(http://mmbiz.qpic.cn/mmbiz/G1lssUsxJOsVVJNUIuKfUP7bLm5EVWxXl5znicMum6Os0CMJHPdeHicicZ4W5MGOVa8ooSXYuE61Ek/0)
    get_data(job_url)

本例子只获取关键字为python,搜索区域为广州,页数为两页的职位信息。(如果读者想爬取多地方多关键词的职位信息,可自行修改)。代码运行后,在文件spider.py同一目录下自动生成text.csv文件,文件内容如下:

现在有了职位信息的数据后,下一步是对这些数据进行分析。数据分析我们采用人工智能的自然语言处理,根据提供的关键词来计算相关词列表。首先对数据进行分词处理,将数据划分为词语。中文分词建议使用jieba模块,分词的效果相当较高,在分词之前,还需要对数据进行清洗,清洗数据中一些标点符号,如下所示:

import csv,re
import jieba
# 数据清洗并分词
csv_reader=csv.reader(open('text.csv',encoding='utf-8'))
seg_list = []
for row in csv_reader:
    temp_list = jieba.cut(row[0], cut_all=False)
    results = re.sub('[()::?“”;.~?/《》【】,,。!()·、.\d ]+', ' ', ' '.join(temp_list))
    seg_list.append(results)
# 将分词写入文件
f = open('data.txt','w',encoding='utf-8')
f.write(' '.join(seg_list))
f.close()

数据清洗完毕会自动保存在文件data.txt,打开data.txt查看数据内容,如图所示。

数据清洗完成后,最后一步就是建模,我们使用gensim模块实现,由word2vec函数方法实现建模,其功能代码如下:

# 通过word2vec计算相关词列表
from gensim import models
# 建模
sentences = models.word2vec.LineSentence('data.txt')
model = models.word2vec.Word2Vec(sentences, size=1000, window=25, min_count=5, workers=4)

# 计算前50个与python相关的词列表
sim = model.wv.most_similar('python', topn=50)
for s in sim:
    print("word:%s,similar:%s " %(s[0],s[1]))

我们将上述的代码写在analysis.py,文件analysis.py的代码如下:
import csv,re
import jieba
from gensim import models
# 数据清洗并分词
csv_reader=csv.reader(open('text.csv',encoding='utf-8'))
seg_list = []
for row in csv_reader:
    temp_list = jieba.cut(row[0], cut_all=False)
    results = re.sub('[()::?“”;.~?/《》【】,,。!()·、.\d ]+', ' ', ' '.join(temp_list))
    seg_list.append(results)

# 将分词写入文件
f = open('data.txt','w',encoding='utf-8')
f.write(' '.join(seg_list))
f.close() 

# 通过word2vec计算相关词列表
# 建模
sentences = models.word2vec.LineSentence('data.txt')
model = models.word2vec.Word2Vec(sentences, size=1000, window=25, min_count=5, workers=4)

# 计算前50个与python相关的词列表
sim = model.wv.most_similar('python', topn=50)
for s in sim:
    print("word:%s,similar:%s " %(s[0],s[1]))

运行analysis.py,输出结果如图所示:

从结果可以看到,要作为一名合格的Python程序员,首先主要掌握Django和scrapy两大框架,selenium是自动化测试技术;数据库以MySQL数据库为主,掌握SQL语句不在话下;掌握memcached缓存系统,linux操作,计算机TCP协议;最后还要涉猎Java,C和Nodejs等一些目前主流开发语言等。

原文发布于微信公众号 - 小詹学Python(xiaoxiaozhantongxue)

原文发表时间:2018-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏零基础使用Django2.0.1打造在线教育网站

零基础使用Django2.0.1打造在线教育网站(二十一):讲师相关页面配置

努力与运动兼备~~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!

1922
来自专栏王清培的专栏

.NET应用架构设计—表模块模式与事务脚本模式的代码编写

阅读目录: 1.背景介绍 2.简单介绍表模块模式、事务脚本模式 3.正确的编写表模块模式、事务脚本模式的代码 4.总结 1.背景介绍 要想正确的设计系统架...

21410
来自专栏沈唁志

你认为该怎么样学习PHP?PHP成长之路

3005
来自专栏Linux驱动

19.Linux-USB总线驱动分析

如下图所示,以windows为例,我们插上一个没有USB设备驱动的USB,就会提示你安装驱动程序 ? 为什么一插上就有会提示信息? 是因为windows自带了...

3868
来自专栏Pytorch实践

【python爬虫】知乎互联网话题问答内容可视化分析

摘要:本文主要针对知乎网站互联网话题下的QA问答对内容进行分析,观察当前互联网话题下用户都比较关注什么。文章从数据爬取、问题分析、高赞答案分析、关键词可视化等方...

3765
来自专栏喵了个咪的博客空间

phalapi-入门篇6(小技巧和浅谈API适用范围以及入门篇总结)

#phalapi-入门篇6(小技巧和浅谈API适用范围以及入门篇总结)# ? ##前言## 先在这里感谢phalapi框架创始人@dogstar,为我们提供了这...

3715
来自专栏搜云库

《深入理解Java虚拟机》(一)Java虚拟机发展史

Java虚拟机发展史 注:本文大部分摘自《深入理解Java虚拟机(第二版)》 作为一名Java开发人员,不能局限于Java语言规范,更需要对Java虚拟机规范有...

25010
来自专栏木头编程 - moTzxx

微信公众平台开发[6] —— 微信开发集成类的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/de...

2013
来自专栏编程微刊

Jquery前端分页插件pagination同步加载和异步加载

3723
来自专栏刁寿钧的专栏

10 分钟梳理关系数据库基础知识(二):存储结构

本文是《十分钟入门关系型数据库》系列技术文章的第二篇,,主要介绍了数据库的存储结构。

7330

扫码关注云+社区

领取腾讯云代金券