爬虫基础

爬虫原理

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()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值