俗话说读万卷书不如行万里路,提升编程能力的很好途径之一是自己去写,但当自己不会的时候去解析别人写的代码也是效率不错的学习途径。
本文将逐行解读大神Alfred数据室里的一项爬取招聘网站的代码,因小弟并非计算机专业,只是略有兴趣,遗漏之处,必为不会,错误不足之处,请不吝指点。
完整代码在文末。
没经验没学历的外教为啥能拿1.4W+的高薪?
本文主要接触python语法中的class的使用,同时略微接触requests和xml库的用法
import requests
import pandas as pd
from lxml import etree
导入第三方库。
其中,requests 库可以用来发送网络请求。
pandas 提供了大量能快速便捷地处理数据的函数和方法。
lxml 可以处理 XML 和 HTML 文件。
class TeachInChina(object):
面向对象编程中的一种基本语法。
def __init__(self, max_page):
self.start_urls = ['http://www.jobleadchina.com/job?job_industry=Teaching' \
'&company_name=&page={}'.format(page) for page in range(1, max_page+1)]
在类的内部,可以定义函数,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例。
start_urls 从哪个网址开始抓取。
format是用来格式化字符串的,format() 方法格式字符串的调用格式为:"{字段名!转换标志:格式说明符}.format()",该代码的意思是将该网址的 page 数值改为后面所要求的。
range(1, max_page+1)]给予了爬取的页码范围,最大值由自己制定。
def get_data(self):
for url in self.start_urls:
res = requests.get(url)
page = url.split('=')[-1]
self.parse_data(res, page)
print('成功爬取并保存第{}页数据!'.format(page))
此处采用循环语法,从第一页访问到设定的最后一页,并使用后面的函数来爬取所需数据。
res = requests.get(url) 即向该网址发送访问请求。
page = url.split(’=’)[-1] 之前提到的 page 由网址的 “=” 为分隔符的最后一段,遇到这种情况不确定,可以直接输入 url 和该代码,直接 print 出来。
@staticmethod
def parse_data(res, page):
if res.status_code == 200:
parsed = etree.HTML(res.text)
title = parsed.xpath('//*[@class="positionTitle"]/a/text()')
link = parsed.xpath('//*[@class="positionTitle"]/a/@href')
salary = [slr.strip() for slr in parsed.xpath('//*[@class="salaryRange"]/text()')]
company = parsed.xpath('//*[@class="companyName"]/a/text()')
area = parsed.xpath('//*[@class="jobThumbnailCompanyIndustry"]/span[3]/text()')
update_time = parsed.xpath('//*[@class="post-time"]/text()')
exp_title = parsed.xpath('//*[@class="jobThumbnailPositionRequire"]/span[3]/text()')
education = parsed.xpath('//*[@class="jobThumbnailPositionRequire"]/span[1]/text()')
com_type = parsed.xpath('//*[@class="jobThumbnailCompanyIndustry"]/span[1]/text()')
data = pd.DataFrame({'title': title, 'link': link, 'salary': salary,
'company': company, 'area': area, 'update_time': update_time,
'exp_title': exp_title, 'education': education,
'com_type': com_type})
if page == '1':
data.to_csv('jobleadchina.csv', index=False, mode='a', header=True)
else:
data.to_csv('jobleadchina.csv', index=False, mode='a', header=False)
else:
print('链接{}请求不成功!'.format(res.url))
@staticmethod 意味着令下面的函数返回静态属性,不强制要求传递参数,观察下面定义的函数,没有写 self.
该函数采用两个条件语句,第一个用来判别是否成功访问以进行数据抓取,第二个用来判别是否为第一页以设置爬取的数据文件格式。
当我们访问网页时,会返回 HTTP 状态码(HTTP status code),成功访问时的值为 200。(文末附状态码对照表)
etree.HTML(res.text) 构造了一个 XPath 解析对象即解析了我们输入的网址。
Xpath 是可以 XML 文档中查找信息的语言,在这里就是搜寻我们所要找的信息,在该语言中:
//* | 选取文档中的所有元素 |
---|---|
@ | 选取属性 |
/a | 获取a标签 |
/text() | 提取文本 |
if __name__ == '__main__':
job = TeachInChina(96)
job.get_data()
name 是当前模块名,当模块被直接运行时模块名为 main 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
job = TeachInChina(96) 就是引入上面的写的class,96为最大页数.
最后一行,执行代码。
class xx(): # 类
def __init__(self): # 类方法必须包含参数self,代表是类的实例
self.xx
@staticmethod # 返回静态属性
def another():
url{字段名!转换标志:格式说明符}.format() # 格式化字符串
url.split('=')[-1] # 选择=后面的最后一段
if __name__ == '__main__': # name是当前模块名,即接下来的内容不可被import
over = xx(?)
over.another()
import requests # 用来访问网站信息
url = {}
res = requests.get(url) # 获取网址
from lxml import etree # 处理XML和HTML
res.status_code == 200 # HTTP status code,200为成功
etree.HTML(res.text) # 构造一个 XPath 解析对象
parsed.xpath('//*[@class="positionTitle"]/a/text()') # //*选取所有元素,@选取属性,/a获取a标签,/text()获取文本
import requests
import pandas as pd
from lxml import etree
class TeachInChina(object):
def __init__(self, max_page):
self.start_urls = ['http://www.jobleadchina.com/job?job_industry=Teaching' \
'&company_name=&page={}'.format(page) for page in range(1, max_page+1)]
def get_data(self):
for url in self.start_urls:
res = requests.get(url)
page = url.split('=')[-1]
self.parse_data(res, page)
print('成功爬取并保存第{}页数据!'.format(page))
@staticmethod
def parse_data(res, page):
if res.status_code == 200:
parsed = etree.HTML(res.text)
title = parsed.xpath('//*[@class="positionTitle"]/a/text()')
link = parsed.xpath('//*[@class="positionTitle"]/a/@href')
salary = [slr.strip() for slr in parsed.xpath('//*[@class="salaryRange"]/text()')]
company = parsed.xpath('//*[@class="companyName"]/a/text()')
area = parsed.xpath('//*[@class="jobThumbnailCompanyIndustry"]/span[3]/text()')
update_time = parsed.xpath('//*[@class="post-time"]/text()')
exp_title = parsed.xpath('//*[@class="jobThumbnailPositionRequire"]/span[3]/text()')
education = parsed.xpath('//*[@class="jobThumbnailPositionRequire"]/span[1]/text()')
com_type = parsed.xpath('//*[@class="jobThumbnailCompanyIndustry"]/span[1]/text()')
data = pd.DataFrame({'title': title, 'link': link, 'salary': salary,
'company': company, 'area': area, 'update_time': update_time,
'exp_title': exp_title, 'education': education,
'com_type': com_type})
if page == '1':
data.to_csv('jobleadchina.csv', index=False, mode='a', header=True)
else:
data.to_csv('jobleadchina.csv', index=False, mode='a', header=False)
else:
print('链接{}请求不成功!'.format(res.url))
if __name__ == '__main__':
job = TeachInChina(96)
job.get_data()
状态码 | 含义 |
---|---|
100 | 客户端应当继续发送请求。 |
101 | 服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求。 |
102 | 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。 |
200 | 请求已成功,请求所希望的响应头或数据体将随此响应返回。 |
201 | 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回。假如需要的资源无法及时建立的话,应当返回 ‘202 Accepted’。 |
202 | 服务器已接受请求,但尚未处理。 |
203 | 服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。 |
204 | 服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。 |
205 | 服务器成功处理了请求,且没有返回任何内容。 |
206 | 服务器已经成功处理了部分 GET 请求。 |
207 | 由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。 |
300 | 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。 |
301 | 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。 |
302 | 请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。 |
304 | 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。 |
305 | 被请求的资源必须通过指定的代理才能被访问。 |
306 | 在最新版的规范中,306状态码已经不再被使用。 |
307 | 请求的资源现在临时从不同的URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。 |
401 | 当前请求需要用户验证。 |
402 | 该状态码是为了将来可能的需求而预留的。 |
403 | 服务器已经理解请求,但是拒绝执行它。 |
404 | 请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。 |
405 | 请求行中指定的请求方法不能被用于请求相应的资源。 |
406 | 请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。 |
407 | 与401响应类似,只不过客户端必须在代理服务器上进行身份验证。代理服务器必须返回一个 Proxy-Authenticate 用以进行身份询问。 |
408 | 请求超时。 |
409 | 由于和被请求的资源的当前状态之间存在冲突,请求无法完成。 |
410 | 被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。 |
411 | 服务器拒绝在没有定义 Content-Length 头的情况下接受请求。 |
412 | 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。 |
413 | 服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。 |
414 | 请求的URI 长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。 |
415 | 对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝。 |
416 | 如果请求中包含了 Range 请求头,并且 Range 中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义 If-Range 请求头,那么服务器就应当返回416状态码。 |
417 | 在请求头 Expect 中指定的预期内容无法被服务器满足,或者这个服务器是一个代理服务器,它有明显的证据证明在当前路由的下一个节点上,Expect 的内容无法被满足。 |
421 | 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。 |
422 | 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。 |
422 | 请求格式正确,但是由于含有语义错误,无法响应。 |
424 | 由于之前的某个请求发生的错误,导致当前请求失败,例如 PROPPATCH。(RFC 4918 WebDAV) |
425 | 在WebDav Advanced Collections 草案中定义,但是未出现在《WebDAV 顺序集协议》(RFC 3658)中。 |
426 | 客户端应当切换到TLS/1.0。(RFC 2817) |
449 | 由微软扩展,代表请求应当在执行完适当的操作后进行重试。 |
500 | 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。 |
501 | 服务器不支持当前请求所需要的某个功能。 |
502 | 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 |
503 | 由于临时的服务器维护或者过载,服务器当前无法处理请求。 |
504 | 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 注意:某些代理服务器在DNS查询超时时会返回400或者500错误 |
505 | 服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体。 |
506 | 由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。 |
507 | 服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。WebDAV (RFC 4918) |
509 | 服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。 |
510 | 获取资源所需要的策略并没有没满足。(RFC 2774) |