爬虫原理
文章目录
1、爬虫的概念
概念:(spider,网络蜘蛛),通过互联网上一个个的网络节点,进行数据的获取
分类:
通用爬虫(了解):
主要用于搜索引擎(百度、google,搜狗等)
搜索引擎工作原理:
核心部分:通用爬虫按照整个互联网的拓扑结构,进行批量的爬取,然后进行数据的清洗与筛选,然后存入百度的数据库
检索部分:提供给用户一个搜索平台,并且按照一定的顺序把关键字相关的信息展现出来
现在的搜索引擎获取数据的方式:
1)通过通用爬虫获取(过程比较缓慢)
2)主动提交自己的url
3)搜索引擎运营商和DNS的运营商合作(向DNS直接要一些有价值的网站)
信息的排名规则:
1)根据流量(用户的点击量)
2)竞价排名,根据一定的规则以及钱财等因素去决定谁在前面
robots协议:(爬虫的一个约定俗成一个协议)
爬虫在取爬取一个网站的时候,首先要读取这个网站robots.txt文件,查看该文件中规定的那些内容可以爬取,那些不可以,在爬取的时候要严格遵从。搜索引擎爬虫在取爬取的时候一定要遵从robots协议,我们写不需要。
聚焦爬虫:
根据客户或者用户的需求,取定制的爬虫,具有比较强的针对性
聚焦爬虫的工作原理:
1、数据的抓取
面临的问题:http协议、url处理等
反爬:用户代理、ip禁止、验证码、会话信息等
2、数据的解析
遇到的数据:html、xml、json
反爬:js动态加载、js加密等
3、数据的存储
csv文件、关系型数据库(mysql)、redis、json等
对于爬虫而言最核心的部分是解决反爬
内容学习
1、python基础
2、相关的库
请求:urllib、requests、scrapy等
解析:正则、xpath、bs4、selenium组件等
3、多任务处理
多进程、多线程、协程
4、分布式爬虫的部署
2、HTTP协议
1、什么是HTTP协议?
1)是基于请求与响应的应用层协议,底层协议TCP保证了数据可靠传输 2)通过url进行客户端与服务器之间的数据交互 3)是一种C/S(B/S)模式的协议,客户端向服务器发起请求,服务器处理请求并且返回响应 4)该协议是一种无状态的协议(不会记录用户的访问状态)
2、http协议过程:
1)创建TCP链接:客户端与服务器的三次握手:客户端向服务器发出一个是否同意创建连接的信号、服务器回应给客户是否空闲(即是否可以创建连接) 、客户端再次向服务器发起创建连接的信息进而创建连接;通过三次握手以后客户端和服务器就创建出了一数据通路,接下来就可以保证http协议包的可靠传输了
2)客户端向服务器发起http请求:通过url把参数(请求体)以及请求头传递给服务器,请求方式有常见4中,常用get和post
请求头:请求头中包含了本次请求的相关的配置信息(比如数据格式、cookie等),决定了客户端和服务器进行数据交流的方式与格式
请求体:就是参数,客户端向服务提交的内容
get和post请求的区别:
形式上:get请求参数拼接在url后面 post请求不体现在url中
内容上:get请求有数据量的限制(不同的浏览器对url最大长度都有不同的限制),post的请求是不限制请求体数据量的(有的web服务器会有一个最大请求体的限制,比如阿帕奇限制为20M)
3)服务器处理请求,并且把处理结果响应给客户端
4)关闭连接:TCP的四次挥手
3、环境
windows或linux,python3.6 , pycharm(sublime), 后期(scrapy和redis数据库)Ubuntu系统
4、fiddler 抓包工具
##5、面试题:
1、请您解释一下什么是http协议?
2、请谈一下get和post的区别
3、http协议和https协议有什么区别?
4、http协议的常见状态码,及其含义?
5、如何取配置https协议?
6、http响应码
常见的http状态码
100:继续 客户端应当继续发送请求。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。
101: 转换协议 在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。只有在切换新的协议更有好处的时候才应该采取类似措施。
102:继续处理 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。
200:请求成功 处理方式:获得响应的内容,进行处理
201:请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到 处理方式:爬虫中不会遇到
202:请求被接受,但处理尚未完成 处理方式:阻塞等待
204:服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。 处理方式:丢弃
300:该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。 处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃
301:请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL
302:请求到的资源在一个不同的URL处临时保存 处理方式:重定向到临时的URL
304:请求的资源未更新 处理方式:丢弃,使用本地缓存文件
400:非法请求 处理方式:丢弃
401:未授权 处理方式:丢弃
403:禁止 处理方式:丢弃
404:没有找到 处理方式:丢弃
500:服务器内部错误 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。
501:服务器无法识别 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
502:错误网关 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
503:服务出错 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。
urllib
urllib.request
import urllib.request
# urllib是python提供的一个用于发起或者处理http请求的框架,它是爬虫的基础框架
# urlib中的request程序包包含了http请求的所有的方法以及配置
url = "http://www.baidu.com/"
# 1、urlopen()方法,用于打开一个远程连接,并且发起请求并返回响应对象
res = urllib.request.urlopen(url=url)
print(res) # <http.client.HTTPResponse object at 0x0000022932488AC8>
# 这个响应对象包含了响应头和响应体
print(res.read().decode('utf-8')) # 读取出响应体
# 2、urlretrieve(url,filename)方法,用于把url网站请求的响应体存入filename所对应文件中
urllib.request.urlretrieve(url=url,filename="./baidu.html")
urllib.request.urlretrieve(url="http://pic10.photophoto.cn/20090204/0036036390702731_b.jpg",filename="./bingbing.jpg")
# 3、urlencode()方法,作用对url的参数进行编码
# url = "https://www.baidu.com/s?ie=utf-8&wd=范冰冰"
# urllib.request.urlopen(url=url) # request框架在请求的时候不接收中文(中文需要编码ascci字符)
url = "https://www.baidu.com/s?"
# 导入parse包,这个包的作用是处理url,以及请求参数等问题
from urllib import parse
# ie=utf-8&wd=xxxx
# 1)把待处理的参数写成字典的形式
dic = {"ie":"utf-8","wd":"老范","laowang":"李晨"}
# 2)用urlencode方法将字典处理成url参数
data = parse.urlencode(dic)
print(data) # ie=utf-8&wd=%E8%80%81%E8%8C%83
# 3)用处理好的参数取提交后台
res = urllib.request.urlopen(url=url+data)
print(res)
url = "https://weibo.cn/"
urllib.request.urlopen(url=url) # urllib.error.HTTPError: HTTP Error 403: Forbidden
# 通过这种方法只能处理一些简单的几乎没有反爬的请求
# 如果某些网站要求我们请求的时候携带足够多的头信息,这里就不能满足
urllib.request, pase
from urllib import request,parse
url = "https://fanyi.baidu.com/sug"
# 对一个post请求需要处理url、请求头、请求体
# 请求头
headers = {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'}
# 请求体,post请求的参数形式也是"参数1=值1&参数2=值2&..."
data = {"kw":"a"}
data = parse.urlencode(data).encode('utf-8')
# 用前面的请求头、请求体和url来创建请求对象
req = request.Request(url=url,headers=headers,data=data)
res = request.urlopen(req)
print(res.read().decode('utf-8'))
##urlib.request,error
from urllib import request,error
url = "http://www.mobiletrain.org/?pinzhuanbdtg=biaoti"
req = request.Request(url=url)
#
# res = request.urlopen(req)
# print(res)
try:
res = request.urlopen(req)
print("0")
except error.HTTPError as e:
# HTTPError,如果请求的时候或者响应的时候发生异常,就会检测到这个对象
print("2")
print(e)
except error.URLError as e:
# 网址本身如果有异常,比如这个域名不存在
print("3")
print(e)
except Exception as e:
print("1")
print(e)
添加请求头
from urllib import request,parse
url = "https://weibo.cn/"
# 反爬:《用户代理》web开发中,同一个url往往可以对应若干个不同版本的网页内容,后台可以根据前端发起的请求的请求头中用户代理(user-agent)的不同,决定响应给前端哪个版本的网页;在反爬的时候可以通过判断前端用户代理是否是我们指定哪几种,如果不是就拒绝访问
# 可以通过设置请求头,在请求头中设置用户代理来把爬虫伪装成浏览器
# 创建请求对象
req = request.Request(url=url,headers={'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'})
# 用请求对象发起请求
res = request.urlopen(req)
print(res)
# 另外:请求头也可以通过下面方式添加
req = request.Request(url=url)
# 通过add方法来给req对象添加请求头
req.add_header("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1")
req.add_header("host","weibo.cn")
#登录时 cookie 的处理
from urllib import request, parse
from http import cookiejar
#处理cookie信息 需要导入cookjar,对cooker信息进行初始化
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
###创建opener + handler 机制
#1 创建一个cookeijar对象
cookie = cookiejar.CookieJar()
#2 创建handler对象 并携带cookerjar
handler = request.HTTPCookieProcessor(cookie)
#3 创建opener 携带handler
opener = request.build_opener(handler)
#登录
longin_url = 'http://www.jokeji.cn/user/c.asp?'
dic = {
'u': 'bobo666',
'p': 'a12345678',
'sn': '1',
't': 'big'
}
params = parse.urlencode(dic)
#发起get请求 登录
req = request.Request(headers=headers, url=longin_url+params)
# res = request.urlopen(req).read()
# print(res)
res = opener.open(req)
print(res)
#访问主页
user_url = 'http://www.jokeji.cn/User/MemberCenter.asp'
req = request.Request(headers=headers, url=user_url)
# user_page = request.urlopen(req)
user_page = opener.open(req)
with open('joke.html', 'wb') as f:
f.write(user_page.read())
open + handler 机制发起请求
from urllib import request
'''
引入opener+handler的机制发起请求
'''
#1、创建一个handler对象
handler = request.HTTPHandler() #用于保存会话信息 , 但是无法携带请求头
#2、 创建opener 对象
opener = request.build_opener(handler) #opener 对象用于携带handler对象 发起请求
url = 'https://www.baidu.com/'
#创建request对象 用于配置请求
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
req = request.Request(headers=headers, url=url)
#3、 用opener 发起请求
res = opener.open(req)
携带代理服务器 的handler
from urllib import request
url = 'https://www.baidu.com/s?wd=ip'
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
#创建一个请求对象,
req = request.Request(url=url, headers=headers)
#代理服务器不存才于请求头中,requset对象无法携带代理服务器,urlopen机制我发携带代理请求
#创建一个携带代理服务器的hanler
hanler = request.ProxyHandler({'https':'125.70.13.77:8080'})
#创建opener
opener = request.build_opener(hanler)
res = opener.open(req)
with open('ip.html', 'wb') as f:
f.write(res.read())
正则简介
'''
正则:
'''
import re
string = '''Hi,girl!
Are you free tonight?'''
# 1 元字符
#普通字符 字母 数字 下划线等
pat = r'o'
res = re.findall(pat, string)
print(res)
#非打印字符
pat = '\n'
res = re.findall(pat, string)
print(res)
#通用元字符
'''
\w 任意一个字母数字下划线
\d 数字
\s 空白
[] 匹配[]中的任意一个 [a-zA-Z0-9]
'''
pat = '\w'
res = re.findall(pat, string)
print(res)
#通配符
'''
. 除换行以外的任意一个字符
^ 从字符串开头匹配
$ 匹配字符串结尾
*
'''
糗事百科抓取段子
from urllib import request, parse
import re
import time
#处理url
def handler_request(url, page):
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
url += str(page)
return request.Request(headers=headers, url=url)
def request_data(req):
res = request.urlopen(req)
return res.read().decode('utf-8')
def anylsis_html(data):
#抓取图片,找图片的url
#pat = r'<div class="thumb">.*?<img'
#模式修正
pat = re.compile(r'<div class="thumb">.*?<img src="(.*?)"', re.S)
res = pat.findall(data)
imgs = []
for url in res:
imgs.append('http:' + url)
return imgs
def main():
url = 'https://www.qiushibaike.com/pic/page/'
#从终端输入带抓取的页面编号
start = input('请输入起始页')
end = input('请输入终止页')
print('正在下载*********')
for i in range(int(start),int(end)+1):
#把每一页处理成一个请求对象
req = handler_request(url=url, page=i)
#发起请求
res = request_data(req)
#解析数据
data = anylsis_html(res)
img_name = 0
for img in data:
print('当前正在下载:' + img)
request.urlretrieve(url=img, filename='./imgs/'+str(img_name)+'.jpg')
img_name +=1
time.sleep(0.1)
print('下载完毕!')
if __name__ == '__main__':
main()
xpath
from lxml import etree
#用etree 读取html 并且创建一个树形结构
html_tree = etree.parse('./test.html')
print(html_tree)
#2 获取节点
li = html_tree.xpath('/html/body/ol/li')
ret = html_tree.xpath('/html/body/div/div/div/a')
#3 找节点的内容和属性
ret = html_tree.xpath('/html/body/div/div/div/a/@href')
#4 定位
#层级定位
#查找所有的li
ret = html_tree.xpath('//div//li/text()')
#查找所有的a
ret = html_tree.xpath('//a/text()')
#属性定位
ret = html_tree.xpath("//div[@class='hh']//a/text()")
#查找所有带id属性的li
ret = html_tree.xpath("//li[@id]/text()")
#查找所有的class属性为dudu的li
ret = html_tree.xpath("//li[@class='dudu']")
ret = html_tree.xpath("//li[@class='haha pp']")
#模糊匹配
#查找所有的class值 以h开头的li
ret = html_tree.xpath("li[starts-with(@class,'h')/@class]")
#包含a的li
ret = html_tree.xpath("li[contains(@class,'a')/@class]")
#逻辑运算
#查找所有 class包含h id包含a的li的内用
ret = html_tree.xpath("//li[contains(@class,'h') and contains(@id,'a')]/text()")
#查找所有 class包含h 或者 id包含a的li的内用
ret = html_tree.xpath("//li[contains(@class,'h') or contains(@id,'a')]/text()")
print(ret)
#bs4
from bs4 import BeautifulSoup
#主要用于网页解析
#把目标网页初始化bs4对象
soup = BeautifulSoup(open('./test.html', encoding='utf-8'), "lxml")
#参数1,代表我们要解析的那个html字符串 参数2 解析器
#1 返回查找到的第一个标签
print(soup.li)
#2 获取属性值
a = soup.a
print(a.get('href'))
# print(a['title'])
#获得所有属性
print(a.attrs)
print(a.name)
#3 获取标签的内容
li = soup.li
print(li.string) #通过string属性获取,只能简单的获取数据,如果有多个标签获取不到
print(li.get_text()) #只提取所有标签的文字内容
body = soup.body
print(body.children) #直接获取子节点
# for note in body.children:
# print(note)
print(body.descendants) #获取后代节点
# for note in body.descendants:
# print(note)
#根据相关的函数查找
print('--- 函数查找----')
#find 函数,返回一个对象
print(soup.find('a'))
print(soup.find('li', id='tata'))
print(soup.find('li', class_='hehe'))
#find_all 函数 返回一个列表
print(soup.find_all('li',class_='dudu'))
#select 函数, 根据css选择器查找
print(soup.select('div.hh')) #组合选择器,先找div 再从div中根据 .hh 查找
print(soup.select('div .hehe')) #包含选择器
print(soup.select('div '))
print(soup.select("[name='btn']")) #根据属性选择
#链家例子 数据爬去与储存
from urllib import request, parse
from bs4 import BeautifulSoup
import json,csv
import pymysql
#定义一个爬虫类
class LianjiaSpider(object):
#重写构造方法
def __init__(self, area, start, end, url):
self.area = area
self.start = start
self.end = end
self.url = url
self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
#1 下载模块
def handler_request(self, page):
page_url = "https://"+self.area + self.url + str(page)
return request.Request(url=page_url, headers=self.headers)
def request_data(self,req):
return request.urlopen(req).read().decode("utf-8")
#2 解析模块
def anylasis_data(self, data):
#bs4解析
soup = BeautifulSoup(data, 'lxml')
house_info = soup.select('li.clear ')
# print(house_info)
#遍历信息
for info in house_info:
#标题
titel = info.select('.title a')[0].get_text()
#房屋
house = info.select('.houseInfo')[0].get_text()
#楼层
flood = info.select('.positionInfo')[0].get_text()
#总价
tatol_price = info.select('.totalPrice')[0].get_text()
#单价
price = info.select('.unitPrice')[0].get_text()
#图片
img = info.select('.lj-lazy')[0].get('data-original')
#信息整合
item = {'title': titel, 'houseInfo': house, 'flood': flood, 'totalPrice': tatol_price, 'unitPrice': price, 'img':img}
yield item
return
#3 存储模块
def write_to_json(self, data):
with open('lianjia.josn', 'w') as f:
f.write(json.dumps(data))
def write_to_csv(self, data):
csv_head = ['title','houseInfo','flood','totalPrice','unitPrice','img']
#整合数据
csv_data = []
for d in data:
item = []
for k, v in d.items():
item.append(v)
csv_data.append(item)
#写入csv
with open('lianjia.csv', 'w') as f:
#创建一个csv的写对象
writer = csv.writer(f)
#写表头
writer.writerow(csv_head)
writer.writerows(csv_data)
def write_to_mysql(self, data):
conn = pymysql.connect(host='60.205.189.255',port=3306,user='biyunsheng', password='17865353093.', db='liumin520',charset='utf8')
cursor = conn.cursor()
#遍历
for dic in data:
sql = 'INSERT INTO lianjia VALUES(NULL ,"%s","%s","%s","%s","%s","%s")' %(dic['title'], dic['houseInfo'], dic['flood'], dic['totalPrice'], dic['unitPrice'], dic['img'])
cursor.execute(sql)
conn.commit()
cursor.close()
conn.close()
#定义一个对外接口, 处理内部的业务逻辑
def crawl_lianjia(self):
#遍历
house_info_list = []
for page in range(int(self.start), int(self.end)+1):
req = self.handler_request(page=page)
res = self.request_data(req)
house_infos = self.anylasis_data(res) #这里得到一个生成器,若干个房屋信息的字典
#存数据,把所有页面的houserInfos中的字典拿出来,重新整合
for info in house_infos:
house_info_list.append(info)
# print(house_info_list)
self.write_to_json(house_info_list)
self.write_to_csv(house_info_list)
self.write_to_mysql(house_info_list)
def main():
#https://bj.lianjia.com/ershoufang/pg5/
url = ".lianjia.com/ershoufang/pg"
area = input("请输入城市(首字母):")
start = input("请输入起始页:")
end = input("请输入终止页:")
#创建爬虫对象,然后爬去
spider = LianjiaSpider(area=area, url=url, start=start, end=end)
spider.crawl_lianjia()
if __name__ == '__main__':
main()
json 存储和分析
import json,jsonpath
books = json.load(open('./book.json','r',encoding='utf-8'))
#print(books['store']['book'])
ret = jsonpath.jsonpath(books, '$.store.book[*].author')
#获取所有的author字典
ret = jsonpath.jsonpath(books, '$..author')
#获取索引为0到1的book字典
ret = jsonpath.jsonpath(books,'$..book[:2]')
boss直聘 bs4
from urllib import request, parse
from bs4 import BeautifulSoup
import csv, pymysql, jsonpath, json,time
#https://www.zhipin.com/job_detail/?query=python&scity=101010100&industry=&position=
class Crawler(object):
def __init__(self, area, start, end, url, query, scity):
self.area = area
self.start = start
self.end = end
self.url = url
self.query = query
self.scity = scity
self.headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
#下载
def handler_request(self, page):
'''
query=python
page=3
scity=10102 0100
'''
url_root = 'https://www.zhipin.com/job_detail/?'
# page_url = url_root + 'query='+ self.query + '&scity='+self.scity + '&page' + str(page)
# print(page_url)
page_url = url_root + 'query=' + parse.quote(self.query) + '&scity=' + self.scity + '&page' + str(page)
print(page_url)
req = request.Request(url=page_url, headers=self.headers)
res = request.urlopen(req).read().decode('utf-8')
return res
#处理数据
def anlysis_data(self,data):
#bs4解析
soup = BeautifulSoup(data, 'lxml')
jobNodes = soup.select('div.job-primary')
# print(len(jobNodes))
for jobNode in jobNodes:
job_name = jobNode.select('div.job-title')[0].get_text()
job_salary = jobNode.select('span.red')[0].get_text()
job_area = jobNode.select('div.info-primary p')[0].get_text()
company_name = jobNode.select('div.info-company a')[0].get_text()
company_data = jobNode.select('div.info-company p')[0].get_text()
publis_name = jobNode.select('div.info-publis h3')[0].get_text()
publis_time = jobNode.select('div.info-publis p')[0].get_text()
item = {'job_name': job_name, 'job_salary': job_salary, 'job_area': job_area, 'company_name': company_name, 'company_data': company_data, 'publis_name': publis_name, 'publis_time': publis_time}
yield item
return
###存储
#json
def write_json(self, data):
with open('./booszhipin.json', 'w') as f:
f.write(json.dumps(data))
#cvs
def write_cvs(self, data):
csv_head = ['job_name','job_salary','job_area','company_name','company_data','publis_name','publis_time']
csv_valuse = []
for dataobj in data:
values = []
for k, v in dataobj.items():
values.append(v)
csv_valuse.append(values)
#写入
with open('./3-5 jinan.csv', 'w') as f:
#csv对象
writer = csv.writer(f)
writer.writerow(csv_head)
writer.writerows(csv_valuse)
def write_mysql(self, data):
conn = pymysql.connect(host='60.205.189.255', port=3306, user='biyunsheng', password='17865353093.', db='liumin520', charset='utf8')
# if self.query == 'python':
# cursor = conn.cursor()
# createsql= "create table %s(id int primary key auto_increment, jobName varchar(255), jobSalary varchar (255), jobAddres varchar(255), companyName varchar(255), companyData varchar(255), publisName varchar(255), publisTime varchar(255));" %self.query
# cursor.execute(createsql)
# conn.commit()
# cursor.close()
# conn.close()
# else:
# pass
cursor = conn.cursor()
for dic in data:
insertSql = 'insert into %s(jobName,jobSalary,jobAddres,companyName,companyData,publisName,publisTime) values("%s","%s","%s","%s","%s","%s","%s");' %(self.query,dic['job_name'],dic['job_salary'],dic['job_area'],dic['company_name'],dic['company_data'],dic['publis_name'],dic['publis_time'])
print(insertSql)
cursor.execute(insertSql)
conn.commit()
cursor.close()
conn.close()
def crawl_zhipin(self):
jobDataList = []
for page in range(int(self.start), int(self.end)+1):
time.sleep(1)
data = self.handler_request(page)
jobDatas = self.anlysis_data(data)
for data in jobDatas:
jobDataList.append(data)
self.write_cvs(jobDataList)
# self.write_json(jobDataList)
# self.write_mysql(jobDataList)
print(len(jobDataList))
def main():
url = 'https://www.zhipin.com/'
# area = input("请输入您的城市")
# start = input("请输入起始页")
# end = input("请输入结束页")
#创建对象
spider = Crawler(url=url, area=0, start=1, end=10, query='教育',scity='101120100')
spider.crawl_zhipin()
if __name__ == '__main__':
main()
selenium
#导入selenium
from selenium import webdriver
from time import sleep
#从selenuim工具中带入驱动模块
#加入无头操作
opt = webdriver.ChromeOptions()
opt.add_argument('--headless')
#创建一个基于驱动chrome的驱动
driver = webdriver.Chrome()
# opt = webdriver.ChromeOptions()
#driver对象可以自动化的从浏览器
#用浏览器发起一个get请求
driver.get('https://www.zhipin.com/')
#点击页面上的某个按钮
# btn = driver.find_elements_by_link_text('新闻')
# btn[0].click()
driver.find_element_by_name("query").send_keys('python')
btn = driver.find_elements_by_tag_name('button')
btn[0].click()
sleep(2)
#输入框中输入内容
# driver.find_element_by_id('kw').send_keys('biyunsheng')
# driver.find_element_by_id('su').click()
# sleep(2)
#对于爬虫,一般要提取某个操作执行结束后,加载出来的网页的源码
#提取浏览器执行完以后得到的html源码
# html = driver.page_source
# with open('4-1 chrome.html', 'w', encoding='utf-8') as f:
# f.write(html)
#关掉浏览器
driver.quit()
from selenium import webdriver
from time import sleep
#下载照片
driver = webdriver.PhantomJS()
driver.get("http://www.1000phone.com/")
driver.save_screenshot('./4-3 phone.png')
#1、bs4
BeaufulSoup 和lxml一样都是用于解析html的框架,对数据的分析和提取。
和lxml相比,效率略低,用起来比较方便
bs4需要下载安装: pip install bs4
#2、jsonPath(了解)
安装:pip install jsonpath
3、selenium
selenium+phatomjs和selenium+chrome
selenium:是一种用于web程序测试的工具,selenium测试的代码可以直接运行在浏览器中,就像真正的用户操作一样。
在写python爬虫的时候,主要是用selenium的webdriver 来驱动浏览器进行相关的操作
安装:pip install selenium
selenium中元素查找:
find_element_by_id()
find_elements_by_name()
find_elements_by_xpath()
find_elements_by_tag_name()
find_elements_by_class_name()
find_elements_by_css_selector()
find_elements_by_link_text()
事件
click() 点击
send_keys()
switch_to_alert()
chromedriver:谷歌浏览器驱动
加载方法如下:
法一:driver = webdriver.Chrome(r'/Users/fanjianbo/Desktop/chromedriver')
法二:把chromedriver的目录配成环境变量路径,然后:driver = webdriver.Chrome()
【注意】chromedriver的版本要和chrome浏览器相对应,不然很多功能不能用
下载操作谷歌浏览器驱动的页面:http://chromedriver.storage.googleapis.com/index.html 或者 http://npm.taobao.org/mirrors/chromedriver/2.37/
谷歌驱动和谷歌浏览器版本之间的映射表:http://blog.csdn.net/huilan_same/article/details/51896672
phantomjs:无界面浏览器
加载方法如下:
法一:driver = webdriver.PhatomJS("C:\Users\ZBLi\Desktop\1706\day04\ziliao\phantomjs-2.1.1-windows\bin\phantomjs.exe")
法二:把phantomjs拷贝到c盘下,并把bin目录配置成环境变量,然后driver = webdriver.PhatomJS()
【注意】phantomjs目前已经不再更新
下载地址:http://phantomjs.org/download.html
4、面试题
1、如何动态加载的数据?
简单的ajax请求:抓包得到ajax的url对该url进行追踪
复杂的动态加载:一般采用selenium+chromedriver或selenium+PhantomJS,进行js代码的解析执行,得到其执行以后html字符串,进一步用xpath、bs4或正则来解析
2、迭代器,生成器,装饰器
迭代器:
生成器:
装饰器:不改变原函数的功能基础上,装饰(增加)新的功能,是的函数可以实现更加广泛的应用。这种设计模式好处,解耦合
3、Python里面如何拷贝一个对象?(赋值,浅拷贝,深拷贝的区别)
赋值:把等号右边的数据,存储到左边变量所开辟的内存空间中
浅拷贝:只拷贝引用不拷贝对象本身,一旦有一个引用修改,所有的引用都会被迫修改
深拷贝:直接拷贝对象本身,产生一个新的对象,并且产生一个新的引用
4、什么是并行和并发?
并行:多个进程在同一时刻同时进行
并发:多个进程在同一时间段内交替进行 (操作系统大多采用并发机制)
5、什么是线程和进程?
进程:一个程序在操作系统中被执行以后就会创建一个进程,通过进程分配资源(cpu、内存、I/O设备),一个进程中会包含一到多个线程,其中有一个线程叫做主线程用于管理其他线程
线程:在一个进程执行的过程,一般会分成很多个小的执行单位,线程就是这些执行单位;在处理机调度,以线程为单位件进行,多个线程之间并发执行,线程占用的是cpu
多线程使用的场合:耗时操作(访问外存,即:I/O,访问网络资源),为了不阻碍主线程或者其他的操作,一般会采用多线程。
6、什么是协程?
协程是:在一个线程执行过程中可以在一个子程序的预定或者随机位置中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。他本身是一种特殊的子程序或者称作函数。
遇到IO密集型的业务时,多线程加上协程,你磁盘在那该读读该写写,我还能去干点别的。在WEB应用中效果尤为明显。
协程的好处:
跨平台
跨体系架构
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序:这一点和事件驱动一样,可以使用异步IO操作来解决
request
##get
import requests
'''
对urllib二次封装,简化请求的复杂操作
'''
#get请求
req = requests.get('http://www.zhaopin.com')
#requerst 不需要任何请求对象
print(req)
# print(req.headers)
# print(req.text)
url = "https://www.baidu.com/s?"
#请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
params = {
'wd': '范冰冰'
}
#发起请求
res = requests.get(url=url, params=params, headers=headers)
print(res.text)
##post
import requests
url = "https://fanyi.baidu.com/sug"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
data = {
"kw": "g"
}
#使用代理proxies={"http": "4552"}
res = requests.post(url=url,data=data,headers=headers, proxies={"http": "4552"})
print(res.text)
#request 携带session登录
import requests
url = "http://www.jokeji.cn/User/Login.asp"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
params = {
"u": "bobo666",
"p": "a12345678",
"sn": "1",
"t": "big"
}
#登录
# res = requests.get(url=url, params=params,headers=headers)
# print(res)
# user_url = "http://www.jokeji.cn/User/MemberCenter.asp/"
# res = requests.get(url=user_url, headers=headers)
# print(res)
#创建一个session()对象 用于携带绘画新奇
s = requests.Session()
#requests
r = s.get(url=url, headers=headers, params=params)
print(r)
res = s.get(url=url)
print(res)
古诗文网 request 实例
import requests
from lxml import etree
#请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
#创建session
login_page = "https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx?type=m"
s = requests.Session()
res = s.get(login_page, headers=headers)
res_tree = etree.HTML(res.text)
#获取token值
a = res_tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0]
b = res_tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0]
#验证码
code_img = "https://so.gushiwen.org" + res_tree.xpath('//img[@id="imgCode"]/@src')[0]
#下载图片
img = s.get(code_img, headers=headers)
code = img.content #获取相应体的二进制
# print(code)
with open('./4-7 yanzhengma.png','wb') as f:
f.write(code)
#人工识别
code_num = input("请查看验证码输入")
login_url = "https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx"
data = {
'__VIEWSTATE':a,
'__VIEWSTATEGENERATOR':b,
'from':'http://so.gushiwen.org/user/collect.aspx?type=m',
'email':'fanjianbo666@163.com',
'pwd':'123456',
'code':code_num,
'denglu':'登录'
}
req = s.post(url=login_url, data=data, headers=headers)
print(req)
request 保持登陆实例
import requests
from bs4 import BeautifulSoup
#请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
login_page = "http://bbs.chinaunix.net/member.php?mod=logging&action=login&logsubmit=yes"
#需要解决的问题
#action是动态的 formhash是动态的
s = requests.Session()
res = s.get(login_page, headers=headers)
login_soup = BeautifulSoup(res.text, 'lxml')
#选取action
action = login_soup.select(" form.cl")[0].get("action")
# print(action)
formhash = login_soup.select("[name='formhash']")[0].get('value')
login_url = "http://bbs.chinaunix.net" + action
#登录
data = {
'formhash': formhash,
'referer': 'http://bbs.chinaunix.net/',
'username': 'MrFan666',
'password': 'f12345678',
'loginsubmit': 'true',
'return_type': ''
}
res = s.post(url=login_url, headers=headers, data=data)
print(res.text)
智联 selenium 实例
from selenium import webdriver
from time import sleep
def anylasis_url(city):
user = input('请输入账户名')
password = input('请输入密码')
city = input('请输入城市全拼')
return 'https://www.zhaopin.com/' + city
#处理数据
def anylasis_data(driver,page):
pass
def anylasis_web(url, jobName,start,end):
urlHome = 'https://www.zhaopin.com/beijing/'
driver = webdriver.Chrome()
driver.get(urlHome)
sleep(0.5)
user = driver.find_elements_by_name('loginname')[0]
passwd = driver.find_elements_by_name('Password')[0]
user.send_keys('17865353093')
passwd.send_keys('biyunsheng1998')
driver.find_elements_by_xpath('//a[@class="me-login__body__btns__submit fl"]')[0].click()
print('正在登录中。。。。。')
sleep(2)
print('关闭弹框')
# driver.find_elements_by_xpath('//img[@src="//desktop-bucket.zhaopin.cn/assets/close.879ac0.png"]')[0].click()
driver.find_elements_by_xpath('//a[@class="close"]')[0].click()
driver.find_elements_by_xpath('//input[@class="zp-search-input"]')[0].send_keys(jobName)
driver.find_elements_by_xpath('//a[@class="zp-search-btn zp-blue-button"]')[0].click()
sleep(2)
print('搜索数据中。。。。')
#页面列表
htmlList = []
for item in range(int(start),int(end)+1):
htmlList.append(item)
html = driver.page_source
page = driver.find_elements_by_xpath('span[@class="soupager__index soupager__index--active"]/text()')
#爬取当前页信息
pageData = []
jobList = driver.find_elements_by_xpath('//div[@class="contentpile__content__wrapper clearfix"]/text()')[0]
for item in jobList:
pageData.append(item)
print(jobList)
# jobList = driver.find_elements_by_id('listItemPile')
'''
id = listItemPile
span = class="soupager__index soupager__index--active 当前页标志
'''
def main():
anylasis_web('', 'python','','')
if __name__ == '__main__':
main()