一名合格的数据分析师分享Python网络爬虫二三事(综合实战案例)

开发 开发工具
作为一名合格的数据分析师,其完整的技术知识体系必须贯穿数据获取、数据存储、数据提取、数据分析、数据挖掘、数据可视化等各大部分。

接上篇文章《一名合格的数据分析师分享Python网络爬虫二三事》

[[184074]]

五、综合实战案例

1. 爬取静态网页数据

(1)需求

爬取豆瓣网出版社名字并分别存储到excel、txt与MySQL数据库中。

(2)分析

  • 查看源码
  • Ctrl+F搜索任意出版社名字,如博集天卷
  • 确定正则模式
  1. "<div class="name">(.*?)</div>"  

(3)思路

  • 下载目标页面
  • 正则匹配目标内容
  • Python列表存储
  • 写入Excel/txt/MySQL

(4)源码

  1. ''信息存储'''import urllibimport reimport xlsxwriterimport MySQLdb#-----------------(1)存储到excel与txt-------------------------#def gxls_concent(target_url,pat): 
  2.     ''' 
  3. 功能:爬取数据 
  4.     @target_url:爬取目标网址 
  5.     @pat:数据过滤模式 
  6.     ''' 
  7.     data = urllib.request.urlopen(target_url).read() 
  8.     reret_concent = re.compile(pat).findall(str(data,'utf-8')) 
  9.     return ret_concentdef wxls_concent(ret_xls,ret_concent): 
  10.     ''' 
  11.     功能:将最终结果写入douban.xls中 
  12.     @ret_xls:最终结果存储excel表的路径 
  13.     @ret_concent:爬取数据结果列表 
  14.     ''' 
  15.     # 打开最终写入的文件 
  16.     wb1 = xlsxwriter.Workbook(ret_xls) 
  17.     # 创建一个sheet工作对象 
  18.     ws = wb1.add_worksheet() 
  19.     try: 
  20.         for i in range(len(ret_concent)): 
  21.             data = ret_concent[i] 
  22.             ws.write(i,0,data) 
  23.         wb1.close() 
  24.     except Exception as er: 
  25.         print('写入“'+ret_xls+'”文件时出现错误') 
  26.         print(er)    def wtxt_concent(ret_txt,ret_concent): 
  27.     ''' 
  28.     功能:将最终结果写入douban.txt中 
  29.     @ret_xls:最终结果存储excel表的路径 
  30.     @ret_concent:爬取数据结果列表 
  31.     ''' 
  32.     fh = open(ret_txt,"wb") 
  33.     try: 
  34.         for i in range(len(ret_concent)): 
  35.             data = ret_concent[i] 
  36.             datadata = data+"\r\n" 
  37.             datadata = data.encode() 
  38.             fh.write(data) 
  39.     except Exception as er: 
  40.         print('写入“'+ret_txt+'”文件时出现错误') 
  41.         print(er)   
  42.     fh.close()def mainXlsTxt(): 
  43.     ''' 
  44.     功能:将数据存储到excel表中 
  45.     ''' 
  46.     target_url = 'https://read.douban.com/provider/all'  # 爬取目标网址 
  47.     pat = '<div>(.*?)</div>' # 爬取模式 
  48.     ret_xls = "F:/spider_ret/douban.xls"   # excel文件路径 
  49.     ret_txt = "F:/spider_ret/douban.txt"   # txt文件路径 
  50.     ret_concent = gxls_concent(target_url,pat) # 获取数据 
  51.     wxls_concent(ret_xls,ret_concent) # 写入excel表 
  52.     wtxt_concent(ret_txt,ret_concent) # 写入txt文件  #---------------------END(1)--------------------------------##-------------------(2)存储到MySQL---------------------------#def db_con(): 
  53.     ''' 
  54.     功能:连接MySQL数据库 
  55.     ''' 
  56.     con = MySQLdb.connect( 
  57.         host='localhost',  # port 
  58.         user='root',       # usr_name 
  59.         passwd='xxxx',     # passname 
  60.         db='urllib_data',  # db_name 
  61.         charset='utf8'
  62.         local_infile = 1 
  63.         ) 
  64.     return con   def exeSQL(sql): 
  65.     ''' 
  66. 功能:数据库查询函数 
  67.     @sql:定义SQL语句 
  68.     ''' 
  69.     print("exeSQL: " + sql) 
  70.     #连接数据库 
  71.     con = db_con() 
  72.     con.query(sql)   def gdb_concent(target_url,pat): 
  73.     ''' 
  74.     功能:转换爬取数据为插入数据库格式:[[value_1],[value_2],...,[value_n]] 
  75.     @target_url:爬取目标网址 
  76.     @pat:数据过滤模式 
  77.     ''' 
  78.     tmp_concent = gxls_concent(target_url,pat) 
  79.     ret_concent = []    
  80.     for i in range(len(tmp_concent)): 
  81.         ret_concent.append([tmp_concent[i]]) 
  82.     return ret_concentdef wdb_concent(tbl_name,ret_concent): 
  83.     ''' 
  84.     功能:将爬取结果写入MySQL数据库中 
  85.     @tbl_name:数据表名 
  86.     @ret_concent:爬取数据结果列表 
  87.     ''' 
  88.     exeSQL("drop table if exists " + tbl_name) 
  89.     exeSQL("create table " + tbl_name + "(pro_name VARCHAR(100));") 
  90.     insert_sql = "insert into " + tbl_name + " values(%s);" 
  91.     con = db_con() 
  92.     cursor = con.cursor() 
  93.     try: 
  94.         cursor.executemany(insert_sql,ret_concent) 
  95.     except Exception as er: 
  96.         print('执行MySQL:"' + str(insert_sql) + '"时出错')         
  97.         print(er) 
  98.     finally: 
  99.         cursor.close()         
  100.         con.commit() 
  101.         con.close()def mainDb(): 
  102.     ''' 
  103.     功能:将数据存储到MySQL数据库中 
  104.     ''' 
  105.     target_url = 'https://read.douban.com/provider/all'  # 爬取目标网址 
  106.     pat = '<div>(.*?)</div>' # 爬取模式 
  107.     tbl_name = "provider" # 数据表名 
  108.     # 获取数据 
  109.     ret_concent = gdb_concent(target_url,pat) 
  110.     # 写入MySQL数据库 
  111.     wdb_concent(tbl_name,ret_concent)  #---------------------END(2)--------------------------------#if __name__ == '__main__': 
  112.     mainXlsTxt() 
  113. mainDb() 


(5)结果

 爬取静态网页数据

2. 爬取基于Ajax技术网页数据

(1)需求

爬取拉勾网广州的数据挖掘岗位信息并存储到本地Excel文件中

(2)分析

a. 岗位数据在哪里?

  • 打开拉勾网==》输入关键词“数据挖掘”==》查看源码==》没发现岗位信息
  • 打开拉勾网==》输入关键词“数据挖掘”==》按F12==》Network刷新==》按下图操作

我们可以发现存在position和company开头的json文件,这很可能就是我们所需要的岗位信息,右击选择open link in new tab,可以发现其就是我们所需的内容。

右击选择open link in new tab

b. 如何实现翻页?

我们在写爬虫的时候需要多页爬取,自动模拟换页操作。首先我们点击下一页,可以看到url没有改变,这也就是Ajax(异步加载)的技术。点击position的json文件,在右侧点击Headers栏,可以发现***部有如下内容:

在右侧点击Headers栏

当我们换页的时候pn则变为2且first变为false,故我们可以通过构造post表单进行爬取。

c. Json数据结构怎么样?

Json数据结构

(3)源码

  1. import urllib.requestimport urllib.parseimport socketfrom multiprocessing.dummy import Poolimport jsonimport timeimport xlsxwriter#----------------------------------------------------------#######(1)获取代理IP###def getProxies(): 
  2.   ''' 
  3.   功能:调用API获取原始代理IP池 
  4.   ''' 
  5.   url = "http://api.xicidaili.com/free2016.txt" 
  6.   i_headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0"} 
  7.   global proxy_addr 
  8.   proxy_addr = [] 
  9.   try: 
  10.       req = urllib.request.Request(url,headers = i_headers
  11.       proxy = urllib.request.urlopen(req).read() 
  12.       proxyproxy = proxy.decode('utf-8') 
  13.       proxyproxy_addr = proxy.split('\r\n')  #设置分隔符为换行符 
  14.   except Exception as er: 
  15.       print(er) 
  16.   return proxy_addr   def testProxy(curr_ip): 
  17.   ''' 
  18.   功能:利用百度首页,逐个验证代理IP的有效性 
  19.   @curr_ip:当前被验证的IP 
  20.   ''' 
  21.   socket.setdefaulttimeout(5)  #设置全局超时时间 
  22.   tarURL = "https://www.baidu.com/"  #测试网址 
  23.   proxy_ip = [] 
  24.   try: 
  25.       proxy_support = urllib.request.ProxyHandler({"http":curr_ip}) 
  26.       opener = urllib.request.build_opener(proxy_support) 
  27.       opener.addheaders=[("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0")] 
  28.       urllib.request.install_opener(opener) 
  29.       res = urllib.request.urlopen(tarURL).read() 
  30.       proxy_ip.append(curr_ip) 
  31.       print(len(res)) 
  32.   except Exception as er: 
  33.       print("验证代理IP("+curr_ip+")时发生错误:"+er) 
  34.   return proxy_ip   def mulTestProxies(proxies_ip): 
  35.   ''' 
  36.   功能:构建多进程验证所有代理IP 
  37.   @proxies_ip:代理IP池 
  38.   ''' 
  39.   pool = Pool(processes=4)  #开启四个进程 
  40.   proxies_addr = pool.map(testProxy,proxies_ip) 
  41.   pool.close() 
  42.   pool.join()  #等待进程池中的worker进程执行完毕 
  43.   return proxies_addr#----------------------------------------------------------#######(2)爬取数据###def getInfoDict(url,page,pos_words_one,proxy_addr_one): 
  44.   ''' 
  45.   功能:获取单页职位数据,返回数据字典 
  46.   @url:目标URL 
  47.   @page:爬取第几页 
  48.   @pos_words_one:搜索关键词(单个) 
  49.   @proxy_addr_one:使用的代理IP(单个) 
  50.   ''' 
  51.   global pos_dict 
  52.   page = 1 
  53.   i_headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0") 
  54.   proxy = urllib.request.ProxyHandler({"http":proxy_addr_one}) 
  55.   opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler) 
  56.   opener.addheaders=[i_headers] 
  57.   urllib.request.install_opener(opener) 
  58.   if page==1: 
  59.       tORf = "true" 
  60.   else: 
  61.       tORf = "false" 
  62.   mydata = urllib.parse.urlencode({"first": tORf,            
  63.                                    "pn": page,           #pn变化实现翻页 
  64.                                    "kd": pos_words_one } ).encode("utf-8") 
  65.   try: 
  66.       req = urllib.request.Request(url,mydata) 
  67.       data=urllib.request.urlopen(req).read().decode("utf-8","ignore")  #利用代理ip打开 
  68.       pos_dict = json.loads(data)  #将str转成dict 
  69.   except urllib.error.URLError  as er: 
  70.       if hasattr(er,"code"): 
  71.           print("获取职位信息json对象时发生URLError错误,错误代码:") 
  72.           print(er.code) 
  73.       if hasattr(er,"reason"): 
  74.           print("获取职位信息json对象时发生URLError错误,错误原因:") 
  75.           print(er.reason) 
  76.   return pos_dictdef getInfoList(pos_dict): 
  77.   ''' 
  78.   功能:将getInfoDict()返回的数据字典转换为数据列表 
  79.   @pos_dict:职位信息数据字典 
  80.   ''' 
  81.   pos_list = []  #职位信息列表    
  82.   jcontent = pos_dict["content"]["positionResult"]["result"]     
  83.   for i in jcontent:         
  84.       one_info = []  #一个职位的相关信息       
  85.       one_info.append(i["companyFullName"])         
  86.       one_info.append(i['companySize'])         
  87.       one_info.append(i['positionName'])         
  88.       one_info.append(i['education'])         
  89.       one_info.append(i['financeStage'])         
  90.       one_info.append(i['salary'])         
  91.       one_info.append(i['city'])         
  92.       one_info.append(i['district'])         
  93.       one_info.append(i['positionAdvantage'])         
  94.       one_info.append(i['workYear'])         
  95.       pos_list.append(one_info) 
  96.   return pos_listdef getPosInfo(pos_words,city_words,proxy_addr): 
  97.   ''' 
  98.   功能:基于函数getInfoDict()与getInfoList(),循环遍历每一页获取最终所有职位信息列表 
  99.   @pos_words:职位关键词(多个) 
  100.   @city_words:限制城市关键词(多个) 
  101.   @proxy_addr:使用的代理IP池(多个) 
  102.   ''' 
  103.   posInfo_result = []     
  104.   title = ['公司全名', '公司规模', '职位名称', '教育程度', '融资情况', "薪资水平", "城市", "区域", "优势", "工作经验"]     
  105.   posInfo_result.append(title)   
  106.   for i in range(0,len(city_words)): 
  107.       #i = 0 
  108.       key_city = urllib.request.quote(city_words[i]) 
  109.       #筛选关键词设置:gj=应届毕业生&xl=大专&jd=成长型&hy=移动互联网&px=new&city=广州 
  110.       url = "https://www.lagou.com/jobs/positionAjax.json?city="+key_city+"&needAddtionalResult=false
  111.       for j in range(0,len(pos_words)): 
  112.           #j = 0 
  113.           page=1 
  114.           while page<10:  #每个关键词搜索拉钩显示30页,在此只爬取10页 
  115.               pos_wordspos_words_one = pos_words[j] 
  116.               #k = 1 
  117.               proxy_addrproxy_addr_one = proxy_addr[page] 
  118.               #page += 1 
  119.               time.sleep(3) 
  120.               pos_info = getInfoDict(url,page,pos_words_one,proxy_addr_one)  #获取单页信息列表 
  121.               pos_infoList = getInfoList(pos_info) 
  122.               posInfo_result += pos_infoList  #累加所有页面信息        
  123.               page += 1    
  124.   return posInfo_result#----------------------------------------------------------#######(3)存储数据###def wXlsConcent(export_path,posInfo_result): 
  125.   ''' 
  126.   功能:将最终结果写入本地excel文件中 
  127.   @export_path:导出路径 
  128.   @posInfo_result:爬取的数据列表 
  129.   ''' 
  130.   # 打开最终写入的文件 
  131.   wb1 = xlsxwriter.Workbook(export_path) 
  132.   # 创建一个sheet工作对象 
  133.   ws = wb1.add_worksheet() 
  134.   try: 
  135.       for i in range(0,len(posInfo_result)): 
  136.           for j in range(0,len(posInfo_result[i])): 
  137.               data = posInfo_result[i][j] 
  138.               ws.write(i,j,data) 
  139.       wb1.close() 
  140.   except Exception as er: 
  141.       print('写入“'+export_path+'”文件时出现错误:') 
  142.       print(er)#----------------------------------------------------------#######(4)定义main()函数###def main(): 
  143.   ''' 
  144.   功能:主函数,调用相关函数,最终输出路径(F:/spider_ret)下的positionInfo.xls文件     
  145.   ''' 
  146.   #---(1)获取代理IP池 
  147.   proxies = getProxies()  #获取原始代理IP    
  148.   proxy_addr = mulTestProxies(proxies) #多线程测试原始代理IP 
  149.   #---(2)爬取数据 
  150.   search_key = ["数据挖掘"]  #设置职位关键词(可以设置多个) 
  151.   city_word = ["广州"]  #设置搜索地区(可以设置多个) 
  152.   posInfo_result = getPosInfo(search_key,city_word,proxy_addr) #爬取职位信息 
  153.   #---(3)存储数据 
  154.   export_path = "F:/spider_ret/positionInfo.xls" #设置导出路径 
  155.   wXlsConcent(export_path,posInfo_result)  #写入到excel中           if __name__ == "__main__": 
  156.   main() 

接下篇文章《一名合格的数据分析师分享Python网络爬虫二三事(Scrapy自动爬虫)》

【本文是51CTO专栏机构“岂安科技”的原创文章,转载请通过微信公众号(bigsec)联系原作者】

戳这里,看该作者更多好文

责任编辑:赵宁宁 来源: 51CTO专栏
相关推荐

2017-02-23 17:46:11

数据分析师Python网络爬虫

2017-02-23 18:41:03

数据分析师Python网络爬虫

2015-08-04 13:25:46

数据分析

2014-06-19 14:00:46

数据分析师

2017-12-11 15:56:44

数据分析师数据仓库数据源

2017-09-19 13:35:47

数据科学数据分析python

2016-11-11 20:38:39

数据分析师大数据

2012-06-08 10:12:08

架构师

2013-04-11 10:03:55

2015-08-18 13:26:05

数据分析

2015-09-30 09:36:58

数据分析师面试offer

2012-08-08 09:00:29

数据分析师

2016-10-21 14:41:22

数据分析师大数据

2022-11-11 11:35:14

2012-08-07 17:32:25

数据分析师

2023-08-25 16:33:10

2019-05-15 15:57:15

Python数据分析爬虫

2013-07-29 15:58:28

大数据数据分析

2017-02-13 19:25:24

2015-04-03 11:19:21

大数据大数据分析师
点赞
收藏

51CTO技术栈公众号