【Python-爬虫】

Python-爬虫

■ 控制台抓包

1、打开浏览器,F12打开控制台,找到Network选项卡
2、控制台常用选项
	2.1、Network: 抓取网络数据包
		1、ALL: 抓取所有的网络数据包
		2、XHR:抓取异步加载的网络数据包
		3、JS : 抓取所有的JS文件
	2、Sources: 格式化输出并打断点调试JavaScript代码,助于分析爬虫中一些参数
	3、Console: 交互模式,可对JavaScript中的代码进行测试
3、抓取具体网络数据包后
	3.1、单击左侧网络数据包地址,进入数据包详情,查看右侧
	3.2、右侧:
		1、Headers: 整个请求信息
			General、Response Headers、Request Headers、Query String、Form Data
		2、Preview: 对响应内容进行预览
		3、Response:响应内容

■ 爬虫分类

■ 1. 通用网络爬虫:(搜索引擎使用,遵守robots协议)

■ robots协议(君子协议)

robots协议:通过robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取
网页后面加 robots.txt 查看网站robots协议。

实例一:www.qq.com/robots.txt
在这里插入图片描述

■ 2. 聚集网络爬虫:自己写的爬虫程序

■ urllib.request(请求模块)

作用: 向网站发送请求,即:我们平时在浏览器输入地址访问网站一样。

函数作用参数
urllib.request.urlopen(URL,timeout)作用URL:需要爬取的URL地址
timeout:设置等待超时时间,指定时间内未响应抛出超时异常。
urllib.request.Request()包装请求,重构User-Agent,使用程序更新正常人类请求URL:请求的URL地址
headers:添加请求头,类型为字典headers= {‘User-Agent’:}
1bytes = response.read() # read()得到结果为 bytes 数据类型
2、string = response.read().decode() # decode() 转为 string 数据类型
3、url = response.geturl() # 返回实际数据的URL地址
4、code = response.getcode() # 返回HTTP响应码
# 补充
5、string.encode() # bytes -> string
6bytes.decode()  # string -> bytes

■ 1. urlopen.py 打开浏览器,输入百度地址(http://www.baidu.com/),得到百度的响应

import urllib.request
# urlopen() : 向URL发请求,返回响应对象
response=urllib.request.urlopen('http://www.baidu.com/')
# 提取响应内容
html = response.read().decode('utf-8')
# 打印响应内容
print(html)

■ User-Agent

作用: User-Agent 有游览器,操作系统信息。

■ 1. 测试网站: http://httpbin.org/get 发送请求,会返回我们的请求头User-Agent内容。

在这里插入图片描述

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0

■ 2. 写的py代码的User-Agent:是Python-urllib/3.7,这样子很容易被服务器知道你是爬虫访问的,所以在发送请求前指定一个User-Agent

'''向http://httpbin.org/get请求,并得到响应'''
import urllib.request

# 得到: 响应对象
res = urllib.request.urlopen('http://httpbin.org/get')
# 获取响应内容
html = res.read().decode()
# 返回实际数据的URL地址
url = res.geturl()
# 返回HTTP响应码
code = res.getcode()
print(html)

==输出结果为如下: 重点是 “User-Agent”: “Python-urllib/3.9”, ==

{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Host": "httpbin.org", 
    "User-Agent": "Python-urllib/3.9", 
    "X-Amzn-Trace-Id": "Root=1-66459722-1898192e214c238340a291e8"
  }, 
  "origin": "120.229.89.12", 
  "url": "http://httpbin.org/get"
}

■ 3. 包装User-Agent,这样服务器就会觉得你是正常访问

import urllib.request
url = 'http://httpbin.org/get'
# 1.创建请求对象
req = urllib.request.Request(
    url=url,
    headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'}
)
# 2.发请求获取响应对象
res = urllib.request.urlopen(req)
# 3.提取响应对象内容
html = res.read().decode()
print(html)

==输出结果为如下: 重点是 “User-Agent”: “Mozilla/5.0”, ==

{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0", 
    "X-Amzn-Trace-Id": "Root=1-664597b1-3331b21109723b253d37e8ca"
  }, 
  "origin": "120.229.89.12", 
  "url": "http://httpbin.org/get"
}

■ 4. from useragents import ua_list 自动获取 User-Agent

import random
from useragents import ua_list

req = request.Request(
	url=url,
	headers={'User-Agent':random.choice(ua_list)}
)
res = request.urlopen(req)
html = res.read().decode('utf-8','ignore')
return html

■ urllib.parse(URL地址编码模块)

■ 1. 简介

作用:给URL地址中查询参数进行编码
编码前:https://www.baidu.com/s?wd=美女
编码后:https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3

# 模块名
urllib.parse
# 导入
import urllib.parse
from urllib import parse

■ 2. urllib.parse.urlencode({dict}) 编码

■ 2.1 URL地址中一个查询参数

# 查询参数:{'wd' : '美女'}
# urlencode编码后:'wd=%e7%be%8e%e5%a5%b3'
# 示例代码
query_string = {'wd' : '美女'}
result = urllib.parse.urlencode(query_string)
# result: 'wd=%e7%be%8e%e5%a5%b3'

■ 2.2 URL地址中多个查询参数

from urllib import parse
query_string_dict = {
	'wd' : '美女',
	'pn' : '50'
}
params = parse.urlencode(query_string_dict)
url = 'http://www.baidu.com/s?{}'.format(params)
print(url)   # http://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3&pn=50

■ 2.3 拼接URL地址的3种方式 (+, %s, format())

# 1、字符串相加
baseurl = 'http://www.baidu.com/s?'
params = 'wd=%E7XXXX&pn=20'
url = baseurl + params
print(url)   # http://www.baidu.com/s?wd=%E7XXXX&pn=20

# 2、字符串格式化(占位符)
params = 'wd=%E7XXXX&pn=20'
url = 'http://www.baidu.com/s?%s'% params
print(url)  # http://www.baidu.com/s?wd=%E7XXXX&pn=20

# 3、format()方法
url = 'http://www.baidu.com/s?{}'
params = 'wd=#E7XXXX&pn=20'
url = url.format(params)
print(url)   # http://www.baidu.com/s?wd=%E7XXXX&pn=20

■ 2.4 拼接url地址发送请求保存html到本地文件

from urllib import request,parse
# 1.拼url地址
# 'http://www.baidu.com/s?wd=??'
url = 'http://www.baidu.com/s?{}'
word = input('请输入搜索内容:')
params = parse.urlencode({'wd':word})
full_url = url.format(params)
# 2.发请求保存到本地
# 1.创建请求对象-Request
# 2.获取响应对象-urlopen
# 3.获取响应内容-readxxxx
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'}
req = request.Request(url=full_url,headers=headers)
res = request.urlopen(req)
html = res.read().decode()
# 保存到本地文件
filename = word + '.html'
with open(filename,'w') as f:
    f.write(html)

■ 3. urllib.parse.quote(‘中文’)

在这里插入图片描述

■ 3.1 parse.quote() 编码示例

from urllib import request,parse
# 1.拼接url地址
# 'http://www.baidu.com/s?wd=??'
url = 'http://www.baidu.com/s?wd={}'
word = input('请输入搜索内容:')
params = parse.quote(word)
full_url = url.format(params)

# 2.发请求保存到本地
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'}
req = request.Request(url=full_url,headers=headers)  # 创建请求对象-Request
res = request.urlopen(req)  # 获取响应对象-urlopen
html = res.read().decode()  # 获取响应内容-readxxxx
# 3.保存到本地文件
filename = word + '.html'
with open(filename,'w',encoding='utf-8') as f:  # 由于网站是encoding='utf-8' 我们打开又是GB2312编码这样内容有中文就会乱码
    f.write(html)

■ 示例一:抓取输入名字百度贴吧的几页的html

from urllib import request,parse
import time
import random
from useragents import ua_list

class TiebaSpider(object):
    def __init__(self):
        self.url = 'http://tieba.baidu.com/f?{}'

    # 请求
    def get_html(self,url):
        req = request.Request(
            url=url,
            headers={'User-Agent':random.choice(ua_list)}
        )
        res = request.urlopen(req)
        html = res.read().decode('utf-8','ignore')

        return html

    # 解析
    def parse_html(self):
        pass

    # 保存
    def save_html(self,filename,html):
        with open(filename,'w',encoding='utf-8') as f:
            f.write(html)

    # 入口函数
    def run(self):
        name = input('请输入贴吧名:')
        begin = int(input('请输入起始页:'))
        stop = int(input('请输入终止页:'))
        # 拼接地址,发请求
        for page in range(begin,stop+1):
            pn = (page-1)*50
            params = {
                'kw' : name,
                'pn' : str(pn)
            }
            params = parse.urlencode(params) # 对字典进行编码
            url = self.url.format(params)
            html = self.get_html(url)
            filename = '{}-第{}页.html'.format(name,page)
            self.save_html(filename,html)
            # 提示
            print('第%d页抓取成功' % page)
            # 每爬取1个页面随机休眠1-3秒钟
            time.sleep(random.randint(1,3))
            time.sleep(random.uniform(0,1))

if __name__ == '__main__':
    start = time.time()
    spider = TiebaSpider()
    spider.run()
    end = time.time()
    print('执行时间:%.2f' % (end-start))

■ 示例二 :猫眼电影-榜单-top100榜

在这里插入图片描述
在这里插入图片描述

from urllib import request
import re
import time
import random
from useragents import ua_list
import csv

class MaoyanSpider(object):
    def __init__(self):
        self.url = 'https://maoyan.com/board/4?offset={}'

    def get_html(self,url):
        headers = {'User-Agent':random.choice(ua_list)}
        req = request.Request(url=url,headers=headers)
        res = request.urlopen(req)
        html = res.read().decode()
        # 直接解析
        self.parse_html(html)
    
    def parse_html(self,html):
        re_bds = '<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?class="releasetime">(.*?)</p>'
        pattern = re.compile(re_bds,re.S)
        # r_list: [('大话西游','周星驰','1994'),(),()]
        r_list = pattern.findall(html)
        self.save_html(r_list)

    # writerow()实现
    # def save_html(self,r_list):
    #     with open('maoyan.csv','a') as f:
    #         writer = csv.writer(f)
    #         for r in r_list:
    #             name = r[0].strip()
    #             star = r[1].strip()[3:]
    #             #上映时间:1994-09-10(加拿大)
    #             time = r[2].strip()[5:15]
    #             L = [name,star,time]
    #             writer.writerow(L)
    #             print(name,time,star)

    # writerows()实现 - [(),(),()]
    def save_html(self, r_list):
        L = []
        with open('maoyan.csv', 'a') as f:
            writer = csv.writer(f)
            for r in r_list:
                name = r[0].strip()
                star = r[1].strip()[3:]
                # 上映时间:1994-09-10(加拿大)
                time = r[2].strip()[5:15]
                L.append((name,star,time))
                print(name, time, star)
            # 在for循环外面
            writer.writerows(L)

    def run(self):
        for offset in range(0,31,10):
            url = self.url.format(offset)
            self.get_html(url)
            time.sleep(random.uniform(1,2))

if __name__ == '__main__':
    spider = MaoyanSpider()
    spider.run()

■ 示例三 :猫眼获取数据保存到mongodb

from urllib import request
import re
import time
import random
from useragents import ua_list
import pymongo

class MaoyanSpider(object):
    def __init__(self):
        self.url = 'https://maoyan.com/board/4?offset={}'
        # 3个对象
        self.conn = pymongo.MongoClient(
            'localhost',27017
        )
        self.db = self.conn['maoyandb']
        self.myset = self.db['maoyanset']

    def get_html(self,url):
        headers = {'User-Agent':random.choice(ua_list)}
        req = request.Request(url=url,headers=headers)
        res = request.urlopen(req)
        html = res.read().decode()
        # 直接解析
        self.parse_html(html)

    def parse_html(self,html):
        re_bds = '<div class="movie-item-info">.*?title="(.*?)".*?<p class="star">(.*?)</p>.*?class="releasetime">(.*?)</p>'
        pattern = re.compile(re_bds,re.S)
        # r_list: [('大话西游','周星驰','1994'),(),()]
        r_list = pattern.findall(html)
        self.save_html(r_list)

    # mongodb数据库: maoyandb -> maoyanset
    def save_html(self, r_list):
        for r in r_list:
            # item定义在for循环里边
            item = {}
            item['name'] = r[0].strip()
            item['star'] = r[1].strip()[3:]
            item['time'] = r[2].strip()[5:15]
            print(item)
            self.myset.insert_one(item)
            # insert_many([{},{},{},{}])

    def run(self):
        for offset in range(0,31,10):
            url = self.url.format(offset)
            self.get_html(url)
            time.sleep(random.uniform(1,2))


if __name__ == '__main__':
    spider = MaoyanSpider()
    spider.run()

■ 示例四 :汽车之家多级页面数据抓取

■ 1. 分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

■ 示例五 :电影天堂二级页面抓取案例 md5加密后保存在数据库后用于判断是否已经爬过的数据。

■ 1. 领取任务

# 地址
电影天堂 - 2019年新片精品 - 更多
# 目标
电影名称、下载链接
# 分析
*********一级页面需抓取***********
1、电影详情页链接
*********二级页面需抓取***********
1、电影名称
2、电影下载链接

■ 2. 实现步骤

  1. 确定响应内容中是否存在所需抓取数据
  2. 找URL规律
1页 :https://www.dytt8.net/html/gndy/dyzz/list_23_1.html
第2页 :https://www.dytt8.net/html/gndy/dyzz/list_23_2.html
第n页 :https://www.dytt8.net/html/gndy/dyzz/list_23_n.html
  1. 写正则表达式
1、一级页面正则表达式
<table width="100%".*?<td width="5%".*?<a href="(.*?)".*?ulink">.*?</table>
2、二级页面正则表达式
<div class="title_all"><h1><font color=#07519a>(.*?)</font></h1></div>.*?<td style="WORDWRAP.*?>.*?>(.*?)</a>
  1. 把电影天堂数据存入MySQL数据库 - 增量爬取
# 思路
# 1、MySQL中新建表 request_finger,存储所有爬取过的链接的指纹
# 2、在爬取之前,先判断该指纹是否爬取过,如果爬取过,则不再继续爬取
  1. 代码实现

先在命令行建库建表

# 建库建表
create database filmskydb charset utf8;
use filmskydb;
create table request_finger(
finger char(32)
)charset=utf8;
create table filmtab(
name varchar(200),
download varchar(500)
)charset=utf8;
from urllib import request
import re
from useragents import ua_list
import time
import random
import pymysql
from hashlib import md5

class FilmSkySpider(object):
    def __init__(self):
        # 一级页面url地址
        self.url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_{}.html'
        self.db = pymysql.connect('192.168.153.151', 'tiger', '123456', 'filmskydb',
        charset='utf8')
        self.cursor = self.db.cursor()

    # 获取html功能函数
    def get_html(self, url):
        headers = {
            'User-Agent': random.choice(ua_list)
        }
        req = request.Request(url=url, headers=headers)
        res = request.urlopen(req)
        # 通过网站查看网页源码,查看网站charset='gb2312'
        # 如果遇到解码错误,识别不了一些字符,则 ignore 忽略掉
        html = res.read().decode('gb2312', 'ignore')
        return html

    # 正则解析功能函数
    def re_func(self, re_bds, html):
        pattern = re.compile(re_bds, re.S)
        r_list = pattern.findall(html)
        return r_list

    # 获取数据函数
    def parse_page(self, one_url):
        html = self.get_html(one_url)
        re_bds = r'<table width="100%".*?<td width="5%".*?<a href="(.*?)".*?ulink">.*?</table>'

        # one_page_list: ['/html/xxx','/html/xxx','/html/xxx']
        one_page_list = self.re_func(re_bds, html)
        for href in one_page_list:
            two_url = 'https://www.dytt8.net' + href
            
            # 生成指纹 - md5加密
            s = md5()
            s.update(two_url.encode())
            two_url_md5 = s.hexdigest()

            # 判断链接是否需要抓取
            if self.is_go_on(two_url_md5):  
                self.parse_two_page(two_url)
                # 爬取完成此链接后将指纹放到数据库表中
                ins = 'insert into request_finger values(%s)'
                self.cursor.execute(ins, [two_url_md5])
                self.db.commit()
                # uniform: 浮点数,爬取1个电影信息后sleep
                time.sleep(random.uniform(1, 3))
		
    def is_go_on(self, two_url_md5):
        # 爬取之前先到数据库中查询比对
        sel = 'select finger from request_finger where finger=%s'
        # 开始抓取之前,先来判断该链接之前是否抓取过
        result = self.cursor.execute(sel, [two_url_md5])
        if not result:
            return True

    # 解析二级页面数据
    def parse_two_page(self, two_url):
        item = {}
        html = self.get_html(two_url)
        re_bds = r'<div class="title_all"><h1><font color=#07519a>(.*?)</font></h1></div>.*?<td style="WORD-WRAP.*?>.*?>(.*?)</a>'

        # two_page_list: [('名称1','ftp://xxxx.mkv')]
        two_page_list = self.re_func(re_bds, html)

        item['name'] = two_page_list[0][0].strip()
        item['download'] = two_page_list[0][1].strip()

        ins = 'insert into filmtab values(%s,%s)'
        film_list = [
            item['name'], item['download']
        ]
        self.cursor.execute(ins, film_list)
        self.db.commit()
        print(film_list)

    def main(self):
        for page in range(1, 201):
            one_url = self.url.format(page)
            self.parse_page(one_url)
            # uniform: 浮点数
            time.sleep(random.uniform(1, 3))

if __name__ == '__main__':
    spider = FilmSkySpider()
    spider.main()

■ requests模块

■ 1. 安装

Linux

sudo pip3 install requests

Windows

# 方法一
进入cmd命令行 :python -m pip install requests
# 方法二
右键管理员进入cmd命令行 :pip install requests
1 2 3 4

■ 2. requests.get() 向网站发起请求,并获取响应对象

# 向网站发起请求,并获取响应对象
res = requests.get(url,headers=headers)

# 参数
1、url :需要抓取的URL地址
2、headers : 请求头
3、timeout : 超时时间,超过时间会抛出异常

# 响应对象(res)属性
1、encoding :响应字符编码
res.encoding = 'utf-8'
2、text :字符串
3、content :字节流
4、status_code :HTTP响应码
5、url :实际数据的URL地址

非结构化数据保存
with open('xxx.jpg','wb') as f:
	f.write(res.content)

■ 3. 保存赵丽颖图片到本地

import requests
url = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1567090051520&di=77e8b97b3280f999cf51340af4315b4b&imgtype
=jpg&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20171121%2F4e6759d153d04c6badbb0a
5262ec103d.jpeg'
headers = {'User-Agent':'Mozilla/5.0'}

html = requests.get(url=url,headers=headers).content
with open('花千骨.jpg','wb') as f:
	f.write(html)

■ 4. requests.get()参数 params

在这里插入图片描述
在这里插入图片描述

import requests
baseurl = 'http://tieba.baidu.com/f?'
params = {
	'kw' : '赵丽颖吧',
	'pn' : '50'
} 
headers = {'User-Agent' : 'Mozilla/4.0'}
# 自动对params进行编码,然后自动和url进行拼接,去发请求
res = requests.get(url=baseurl,params=params,headers=headers)
res.encoding = 'utf-8'
print(res.text)

■ 5. requests.get()参数 SSL证书认证参数-Verify

1、适用网站: https类型网站但是没有经过 证书认证机构 认证的网站
2、适用场景: 抛出 SSLError 异常则考虑使用此参数

1、verify=True(默认) : 检查证书认证
2、verify=False(常用): 忽略证书认证
# 示例
response = requests.get(
	url=url,
	params=params,
	headers=headers,
	verify=False
)

在这里插入图片描述

■ 5. requests.get()参数 -代理参数-proxies

■ 5.1 定义

1、定义: 代替你原来的IP地址去对接网络的IP地址。
2、作用: 隐藏自身真实IP,避免被封。

■ 5.2 获取代理IP网站

	西刺代理、快代理、全网代理、代理精灵、... ...

■ 5.3 参数类型

1、语法结构
proxies = {
	'协议':'协议://IP:端口号'
}

2、示例
proxies = {
	'http':'http://IP:端口号',
	'https':'https://IP:端口号'
}

■ 5.3 示例一: 使用免费普通代理IP访问测试网站: http://httpbin.org/get

import requests

url = 'http://httpbin.org/get'
headers = {
	'User-Agent':'Mozilla/5.0'
} 
#定义代理,在代理IP网站中查找免费代理IP
proxies = {
	'http':'http://112.85.164.220:9999',
	'https':'https://112.85.164.220:9999'
} 
html = requests.get(url,proxies=proxies,headers=headers,timeout=5).text
print(html)

■ 5.3 建立一个自己的代理IP池,随时更新用来抓取网站数据

1、从西刺代理IP网站上,抓取免费代理IP
2、测试抓取的IP,可用的保存在文件中
import requests
from lxml import etree
import time
import random
from fake_useragent import UserAgent

class GetProxyIP(object):
    def __init__(self):
        self.url = 'https://www.xicidaili.com/nn/{}'

    # 随机生成1个User-Agent
    def get_headers(self):
        ua = UserAgent()
        useragent = ua.random
        headers = {'User-Agent':useragent}
        return headers

    # 获取可用代理IP文件
    def get_ip_file(self,url):
        headers = self.get_headers()
        html = requests.get(url=url,headers=headers,timeout=5).text

        parse_html = etree.HTML(html)
        tr_list = parse_html.xpath('//tr')
        for tr in tr_list[1:]:
            ip = tr.xpath('./td[2]/text()')[0]
            port = tr.xpath('./td[3]/text()')[0]
            # 测试ip:port是否可用
            self.test_ip(ip,port)

    def test_ip(self,ip,port):
        proxies = {
            'http':'http://{}:{}'.format(ip,port),
            'https': 'https://{}:{}'.format(ip, port),
        }
        test_url = 'http://www.baidu.com/'
        try:
            res = requests.get(url = test_url,proxies = proxies,timeout = 8)
            if res.status_code == 200:
            print(ip,port,'Success')
            with open('proxies.txt','a') as f:
            f.write(ip + ':' + port + '\n')
        except Exception as e:
            print(ip,port,'Failed')

    # 主函数
    def main(self):
        for i in range(1, 1001):
            url = self.url.format(i)
            self.get_ip_file(url)
            time.sleep(random.randint(5, 10))


if __name__ == '__main__':
    spider = GetProxyIP()
    spider.main()

■ 5.4

import requests

class ProxyPool(object):
    def __init__(self):
        self.url = 'http://dev.kdlapi.com/api/getproxy/?orderid=967108906914177&num=55&protocol=2&method=2&an_an=1&an_ha=1&sep=1'
        self.headers = {'User-Agent':''}

    def save_proxy(self):
        html = requests.get(url=self.url,headers=self.headers).text
        # ip_list: ['1.1.1.1:8888','']
        ip_list = html.split('\r\n')
        print(ip_list)
        for ip in ip_list:
            # 测试ip,可用的保存到文件
            if self.test_ip(ip):
                # 把ip写入到文件
                with open('ip.txt','a') as f:
                    f.write(ip + '\n')

    # 测试代理ip是否可用
    def test_ip(self,ip):
        proxies = {
            'http':'http://{}'.format(ip),
            'https':'https://{}'.format(ip)
        }
        try:
            res = requests.get(
                url='http://www.baidu.com/',
                proxies=proxies,
                timeout=2
            )
            if res.status_code == 200:
                print(ip,'可用')
                return True
        except Exception as e:
            print(ip,'不可用')
            return False

if __name__ == '__main__':
    spider = ProxyPool()
    spider.save_proxy()

■ 5.5 写一个获取收费开放代理的接口

# 获取开放代理的接口
import requests

def test_ip(ip):
    url = 'http://www.baidu.com/'
    proxies = {
        'http':'http://{}'.format(ip),
        'https':'https://{}'.format(ip),
    }

    try:
        res = requests.get(url=url,proxies=proxies,timeout=8 )
        if res.status_code == 200:
            return True
    except Exception as e:
        return False


# 提取代理IP
def get_ip_list():
    api_url = 'http://dev.kdlapi.com/api/getproxy/?orderid=946562662041898&num=100&protocol=1&method=2&an_an=1&an_ha=1&sep=2'
    html = requests.get(api_url).content.decode('utf-8','ignore')
    # ip_port_list: ['IP:PORT','IP:PORT','']
    ip_port_list = html.split('\r\n')
    # 依次遍历代理IP,并进行测试
    for ip in ip_port_list:
        with open('proxy_ip.txt','a') as f:
            if test_ip(ip):
                f.write(ip + '\n')

if __name__ == '__main__':
    get_ip_list()

■ 5.6 私密代理

1、语法结构
proxies = {
	'协议':'协议://用户名:密码@IP:端口号'
} 

2、示例
proxies = {
	'http':'http://用户名:密码@IP:端口号',
	'https':'https://用户名:密码@IP:端口号'
}

示例代码

import requests
url = 'http://httpbin.org/get'
proxies = {
	'http': 'http://309435365:szayclhp@106.75.71.140:16816',
	'https':'https://309435365:szayclhp@106.75.71.140:16816',
} 
headers = {
	'User-Agent' : 'Mozilla/5.0',
} 
html = requests.get(url,proxies=proxies,headers=headers,timeout=5).text
print(html)

■ 6. requests.get()参数 -cookie 抓取需要登录才能访问的页面

抓取需要登录才能访问的页面

cookie和session机制
http协议为无连接协议
cookie: 存放在客户端浏览器
session: 存放在Web服务器

■ 6.1 方法一 人人网登录案例 -登录网站手动抓取Cookie

1、先登录成功1,获取到携带登录信息的Cookie
登录成功 - 个人主页 - F12抓包 - 刷新个人主页 - 找到主页的包(profile)
2、携带着cookie发请求
	** Cookie
	** User-Agent

# 1、将self.url改为 个人主页的URL地址
# 2、将Cookie的值改为 登录成功的Cookie值

# (.*): (.*)
# "$1": "$2",
# //*[@id="operate_area"]/div[1]/ul/li[1]/span
import requests
from lxml import etree

class RenrenLogin(object):
    def __init__(self):
        self.url = 'http://www.renren.com/969255813/profile'
        self.headers = {
            "Cookie": "td_cookie=18446744071755925920; anonymid=k1u1d9w1-74iox1; depovince=GW; _r01_=1; JSESSIONID=abcB2p2gp7PgCepRLxx3w; ick_login=21331dc6-741c-48b0-9bc0-2b108f6537ad; first_login_flag=1; ln_uact=18633615542; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; jebe_key=9c4bdeee-d396-4774-9804-a9ec189e321a%7C2e9beece3ead42fe6a26739d515f14df%7C1571276385847%7C1%7C1571276390161; jebe_key=9c4bdeee-d396-4774-9804-a9ec189e321a%7C2e9beece3ead42fe6a26739d515f14df%7C1571276385847%7C1%7C1571276390164; wp_fold=0; td_cookie=18446744071756139140; jebecookies=ba19300e-2925-4b13-b8af-d21ee45da052|||||; _de=2229A2704041535FC5E7FC3B0F076082; p=db307493030c61b1e1b3b498a780655b3; t=4fe365d1adf8179ea16550ae0b895aab3; societyguester=4fe365d1adf8179ea16550ae0b895aab3; id=969255813; xnsid=28993c4d; ver=7.0; loginfrom=null",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36",
        }

    def parse_html(self):
        html = requests.get(
            url=self.url,
            headers=self.headers
        ).text
        p = etree.HTML(html)
        xpath_bds = '//*[@id="operate_area"]/div[1]/ul/li[1]/span/text()'
        r_list = p.xpath(xpath_bds)

        print(r_list)

if __name__ == '__main__':
    spider = RenrenLogin()
    spider.parse_html()

■ 6.2 方法二 把抓取到的cookie处理为字典 使用requests.get()中的参数:cookies

处理cookie为字典

# 处理cookies为字典
cookies_dict = {}
cookies = 'xxxx'
for kv in cookies.split('; ')
	cookies_dict[kv.split('=')[0]] = kv.split('=')[1]
import requests
from lxml import etree

class RenrenLogin(object):
	def __init__(self):
		self.url = 'http://www.renren.com/967469305/profile'
		self.headers = {
		'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like	Gecko) Chrome/76.0.3809.100 Safari/537.36'

# 获取字典形式cookie的函数
def get_cookie_dict(self):
	cookie_dict = {}
	# 自己抓到的cookie
	cookies = 'xxxxxxx'
	for kv in cookies.split('; '):
		# kv: 'td_cookie=184xxx'
		key = kv.split('=')[0]
		value = kv.split('=')[1]
		cookie_dict[key] = value
		return cookie_dict

def get_html(self):
	# 获取cookies
	cookies = self.get_cookie_dict()
	html = requests.get(
		url=self.url,
		headers=self.headers,
		cookies=cookies,
	).text
	self.parse_html(html)
	
def parse_html(self,html):
	parse_html = etree.HTML(html)
	r_list = parse_html.xpath('//*[@id="operate_area"]/div[1]/ul/li[1]/span/text()')
	print(r_list)
	
if __name__ == '__main__':
	spider = RenrenLogin()
	spider.get_html()
}

■ 6.3 方法一 requests模块处理Cookie

在这里插入图片描述
在这里插入图片描述

# 把Formdata中的 email 和 password 的改为自己真实的用户名和密码
import requests
from lxml import etree

class RenrenSpider(object):
	def __init__(self):
		self.post_url = 'http://www.renren.com/PLogin.do'
		self.get_url = 'http://www.renren.com/967469305/profile'
		# 实例化session对象
		self.session = requests.session()
	
	def get_html(self):
		# email和password为<input>节点中name的属性值
		form_data = {
			'email' : 'xxxx',
			'password' : 'xxxx'
		} 
		# 先session.post()
		self.session.post(url=self.post_url,data=form_data)
		# 再session.get()
		html = self.session.get(url=self.get_url).text
		self.parse_html(html)
		
	def parse_html(self,html):
		parse_html = etree.HTML(html)
		r_list = parse_html.xpath('//li[@class="school"]/span/text()')
		print(r_list)
	
if __name__ == '__main__':
	spider = RenrenSpider()
	spider.get_html()

■ 7 requests.post()参数

Post类型请求的网站

■ 7.1 参数-data

response = requests.post(url,data=data,headers=headers)
# data :post数据(Form表单数据-字典格式)

■ 7.2 请求方式的特点

# 一般
GET请求 : 参数在URL地址中有显示
POST请求: Form表单提交数据

■ 7.4 有道翻译破解案例(post)

import requests
import time
import random
from hashlib import md5

class YdSpider(object):
    def __init__(self):
        # 1.url一定要写F12抓到的POST的地址: Request URL
        self.url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
        self.headers = {
            # 2.检查频率最高的三个
            "Cookie": "OUTFOX_SEARCH_USER_ID=584508170@10.108.160.19; OUTFOX_SEARCH_USER_ID_NCOO=415742579.8667161; JSESSIONID=aaa6xixEY8dfdcuUIHo3w; ___rl__test__cookies=1571131223483",
            "Referer": "http://fanyi.youdao.com/",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36",
        }
        self.proxies = {
            'http': 'http://309435365:szayclhp@123.56.246.33:16816',
            'https': 'https://309435365:szayclhp@123.56.246.33:16816'
        }

    # 获取ts,salt,sign
    def get_ts_salt_sign(self,word):
        ts = str(int(time.time()*1000))
        salt = ts + str(random.randint(0,9))
        string = "fanyideskweb" + word + salt + "n%A-rKaT5fb[Gy?;N5@Tj"
        s = md5()
        s.update(string.encode())
        sign = s.hexdigest()

        return ts,salt,sign

    def attack(self,word):
        ts,salt,sign = self.get_ts_salt_sign(word)
        # form表单数据
        data = {
            "i": word,
            "from": "AUTO",
            "to": "AUTO",
            "smartresult": "dict",
            "client": "fanyideskweb",
            "salt": salt,
            "sign": sign,
            "ts": ts,
            "bv": "5d4cb17cceb9ecd02ece3ed9923d3a7a",
            "doctype": "json",
            "version": "2.1",
            "keyfrom": "fanyi.web",
            "action": "FY_BY_REALTlME",
        }
        # 此处为post请求,一定使用 reqeusts.post()方法
        html = requests.post(
            url=self.url,
            data=data,
            headers=self.headers,
            proxies=self.proxies
        ).json()
        print('翻译结果:',html['translateResult'][0][0]['tgt'])

    def run(self):
        word = input('请输入要翻译的单词:')
        self.attack(word)

if __name__ == '__main__':
    spider = YdSpider()
    spider.run()

■ 7.5 民政部网站数据抓取

import requests
from lxml import etree
import re

class GovSpider(object):
    def __init__(self):
        self.url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'
        self.headers = {
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
            'Cookie':'td_cookie=18446744071602793018; td_cookie=18446744071601712718; _gscu_190942352=711200480urfj316; _gscbrs_190942352=1; td_cookie=18446744071602559600; wzws_cid=7c25b6f33e355504f8779f50aed809a99f6c76ad8aa92c93a43d8c6f1eab82a8deba674257edf01f29378319c08ade1f67e5d099d66adab79554be470a97d993; _gscs_190942352=t71122459w1kg4p19|pv:4'
        }
        self.proxies = {
            'http': 'http://309435365:szayclhp@123.56.246.33:16816',
            'https': 'https://309435365:szayclhp@123.56.246.33:16816'
        }

    # 获取最新链接
    def get_link(self):
        one_html = requests.get(url=self.url,headers=self.headers,proxies=self.proxies).content.decode()

        # xpath提取最新链接
        p = etree.HTML(one_html)
        link_list = p.xpath('//a[@class="artitlelist"]/@href')
        link = link_list[1] if link_list else None
        if link:
            # link为假链接
            link = 'http://www.mca.gov.cn' + link
            # 通过假链接link来提取真链接
            self.get_real_link(link)
        else:
            print('提取链接失败')

    # 提取真实链接
    def get_real_link(self,link):
        html = requests.get(
            url=link,
            proxies=self.proxies,
            headers=self.headers
        ).content.decode()
        # 正则解析提取
        re_bds = 'window.location.href="(.*?)"'
        p = re.compile(re_bds,re.S)
        real_link = p.findall(html)[0]
        # 提取数据
        self.parse_html(real_link)

    # 提取数据
    def parse_html(self,real_link):
        two_html = requests.get(url=real_link,headers=self.headers).text
        p = etree.HTML(two_html)

        # 基准xpath: 节点对象列表
        tr_list = p.xpath('//tr[@height="19"]')
        for tr in tr_list:
            name = tr.xpath('./td[3]/text()')[0].strip()
            code = tr.xpath('./td[2]/text()')[0].strip()
            print(name,code)

    # 入口函数
    def run(self):
        self.get_link()

if __name__ == '__main__':
    spider = GovSpider()
    spider.run()

■ 7.6 动态加载数据抓取-Ajax

■ 7.7 豆瓣电影数据抓取案例

import requests
import json
from fake_useragent import UserAgent
import re

class DoubanSpider(object):
    def __init__(self):
        self.url = 'https://movie.douban.com/j/chart/top_list?'
        self.i = 0

    def parse_html(self,params):
        html = requests.get(
            url=self.url,
            params=params,
            headers={ 'User-Agent':UserAgent().random }
        ).text
        # json.loads(): 把json数据->Python数据类型
        html = json.loads(html)
        # 提取数据
        item = {}
        for film in html:
            item['name'] = film['title']
            item['score'] = film['score']
            print(item)
            self.i += 1

    def run(self):
        type_dict = self.get_all()
        menu = ''
        for key in type_dict.keys():
            # key: '剧情' '喜剧'
            menu += ' {} '.format(key)

        print(menu)
        name = input('请输入电影类别:')
        # 得到了type的值
        typ = type_dict[name]

        total = self.get_total(typ)
        # 100个电影 range(0,100,20)
        for page in range(0,total,20):
            params = {
                'type': typ,  # 电影类型
                'interval_id': '100:90',
                'action': '',
                'start': str(page),  # 每次加载电影的起始索引值 0 20 40 60
                'limit': '20'  # 每次加载的电影数量
            }
            self.parse_html(params)
        print('电影总数:',self.i)

    def get_all(self):
        url = 'https://movie.douban.com/chart'
        html = requests.get(
            url=url,
            headers={'User-Agent':UserAgent().random}
        ).text
        p = re.compile('<a href=".*?type_name=(.*?)&type=(.*?)&.*?</a>',re.S)
        r_list = p.findall(html)
        # [('剧情','5'),('喜剧','24')]
        type_dict = {}
        for r in r_list:
            type_dict[r[0]] = r[1]

        return type_dict

    def get_total(self,typ):
        url = 'https://movie.douban.com/j/chart/top_list_count?type={}&interval_id=100%3A90'.format(typ)
        html = requests.get(
            url=url,
            headers={ 'User-Agent':UserAgent().random }
        ).json()
        total = html['total']

        return total


if __name__ == '__main__':
    spider = DoubanSpider()
    spider.run()

■ 增量爬虫Redis

■ 1. 原理

在这里插入图片描述

■ 互联网图片抓取

■ 示例一: 通过链接获取图片,抓取下来,保存在本地

"""
通过链接获取图片,抓取下来,保存在本地
"""
import requests

image_url = 'https://cbu01.alicdn.com/img/ibank/O1CN016VtvDr1iK2kLm5l0R_!!933764393-0-cib.jpg'
# headers = {'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'}

# 1, content属性:获取bytes数据类型
html = requests.get(url=image_url).content

# 2. 保存到本地文
# filename = image_url[-10:]     # 截取最后10位数据作为文件名称保存
# print(filename)
# with open(filename ,'wb') as f:

with open('meitu.jpg' ,'wb') as f:
    f.write(html)

■ 示例二:使用os 获取图片,抓取下来,保存到images文件夹中

"""
使用os 获取图片,抓取下来,保存images文件夹中
"""
import requests
import os

image_url = 'https://cbu01.alicdn.com/img/ibank/O1CN016VtvDr1iK2kLm5l0R_!!933764393-0-cib.jpg'
# headers = {'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0'}

# 1, content属性:获取bytes数据类型
html = requests.get(url=image_url).content

# 2. 确保图片保存路径
directory = './images/'
if not os.path.exists(directory):
    os.makedirs(directory)

# 3. 保存到本地文
filename = directory+image_url[-10:]     # 截取最后10位数据作为文件名称保存
print(filename)
with open(filename,'wb') as f:
    f.write(html)

■ 示例三:百度图片练习代码实现,创建目录,准备保存图片

import requests
import re
from urllib import parse
import os

class BaiduImageSpider(object):
    def __init__(self):
        self.url = 'https://image.baidu.com/search/index?tn=baiduimage&word={}'
        self.headers = {'User-Agent':'Mozilla/5.0'}

    # 获取图片
    def get_image(self,url,word):
        html = requests.get(url,headers=self.headers).text
        pattern = re.compile('"hoverURL":"(.*?)"',re.S)
        img_link_list = pattern.findall(html)
        # 创建目录,准备保存图片
        directory = 'E:\\{}\\'.format(word)
        if not os.path.exists(directory):
            os.makedirs(directory)

        i = 1
        for img_link in img_link_list:
            filename = '{}{}_{}.jpg'.format(directory, word, i)
            self.save_image(img_link,filename)
            i += 1

    def save_image(self,img_link,filename):
        html = requests.get(url=img_link,headers=self.headers).content
        with open(filename,'wb') as f:
            f.write(html)
        print(filename,'下载成功')

    def run(self):
        word = input('你要谁的照片:')
        word_parse = parse.quote(word)
        url = self.url.format(word)
        self.get_image(url,word)

if __name__ == '__main__':
    spider = BaiduImageSpider()
    spider.run()

■ Chrome浏览器插件

1、Xpath Helper: 轻松获取HTML元素的xPath路径
# 开启/关闭: Ctrl + Shift + x
2、Proxy SwitchyOmega: Chrome浏览器中的代理管理扩展程序
3、JsonView: 格式化输出json格式数据

■ xpath

■ 1. 简介

XPath即为XML路径语言,它是一种用来确定XML文档中某部分位置的语言,同样适用于HTML文档的检索

■ 2. 选取节点

1// :从所有节点中查找(包括子节点和后代节点)
2、@ :获取属性值
	# 使用场景1(属性值作为条件)
	//div[@class="movie-item-info"]
	# 使用场景2(直接获取属性值)
	//div[@class="movie-item-info"]/a/img/@src

■ 3. 匹配多路径(或)

xpath表达式1 | xpath表达式2 | xpath表达式3

■ 4. 常用函数

1、contains() :匹配属性值中包含某些字符串节点
	# 查找id属性值中包含字符串 "car_" 的 li 节点
	//li[contains(@id,"car_")]
2、text() :获取节点的文本内容
	# 查找所有汽车的价格
	//li/p[@class="price"]/text()

■ 5. 匹配演示

<ul class="CarList">
    <li class="bjd" id="car_001" href="http://www.bjd.com/">
        <p class="name">布加迪</p>
        <p class="model">威航</p>    
        < p class ="price" > 2500万 </ p>
        < p class ="color" > 红色 </ p>
    </ li>

    <li class ="byd" id="car_002" href="http://www.byd.com/">
        <p class ="name" > 比亚迪 </ p>
        <p class ="model" > 秦 </ p>
        <p class ="price" > 15万 </ p>
        <p class ="color" > 白色 </ p>
    </ li>
</ ul>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1、查找所有的li节点
//li
2、获取所有汽车的名称: 所有li节点下的子节点p的值 (class属性值为name)
//li/p[@class="name"]
3、找比亚迪车的信息: 获取ul节点下第2个li节点的汽车信息
//ul/li[2]
4、获取所有汽车的链接: ul节点下所有li子节点的href属性的值
//ul/li/@href
# 只要涉及到条件,加 []
# 只要获取属性值,加 @

■ lxml解析库

■ 1. 安装

Ubantu安装: sudo pip3 install lxml
Window安装: python -m pip install lxml

■ 2. 使用流程

1、导模块
	from lxml import etree
2、创建解析对象
	parse_html = etree.HTML(html)
3、解析对象调用xpath
	r_list = parse_html.xpath('xpath表达式')

■ 3. 示例一:lxml + xpath

from lxml import etree

html = '''
<div class="wrapper">
	<a href="/" id="channel">新浪社会</a>
	<ul id="nav">
		<li>
		    <a href="http://domestic.sina.com/" title="国内">国内</a>
		</li>
		<li><a href="http://world.sina.com/" title="国际">国际</a></li>
		<li><a href="http://mil.sina.com/" title="军事">军事</a></li>
		<li><a href="http://photo.sina.com/" title="图片">图片</a></li>
		<li><a href="http://society.sina.com/" title="社会">社会</a></li>
		<li><a href="http://ent.sina.com/" title="娱乐">娱乐</a></li>
		<li><a href="http://tech.sina.com/" title="科技">科技</a></li>
		<li><a href="http://sports.sina.com/" title="体育">体育</a></li>
		<li><a href="http://finance.sina.com/" title="财经">财经</a></li>
		<li><a href="http://auto.sina.com/" title="汽车">汽车</a></li>
	</ul>
</div>
'''

parse_html = etree.HTML(html)
xpath_bds = '//ul[@id="nav"]//a/text()'
r_list = parse_html.xpath(xpath_bds)
print(r_list)  # ['国内', '国际', '军事', '图片', '社会', '娱乐', '科技', '体育', '财经', '汽车']

# 获取所有a节点 href 的属性值 ['/','https://xxx']
xpath_bds = '//a/@href'
r_list = parse_html.xpath(xpath_bds)
print(r_list)

# 获取所有a节点 href 的属性值,但是不包含 /
xpath_bds = '//ul[@id="nav"]/li/a/@href'
r_list = parse_html.xpath(xpath_bds)
print(r_list)

# 获取 国际、国内、军事...,但是不包含 新浪社会
xpath_bds = '//ul[@id="nav"]/li/a/text()'
r_list = parse_html.xpath(xpath_bds)
print(r_list)

# 输出结果
['国内', '国际', '军事', '图片', '社会', '娱乐', '科技', '体育', '财经', '汽车']
['/', 'http://domestic.sina.com/', 'http://world.sina.com/', 'http://mil.sina.com/', 'http://photo.sina.com/', 'http://society.sina.com/', 'http://ent.sina.com/', 'http://tech.sina.com/', 'http://sports.sina.com/', 'http://finance.sina.com/', 'http://auto.sina.com/']
['http://domestic.sina.com/', 'http://world.sina.com/', 'http://mil.sina.com/', 'http://photo.sina.com/', 'http://society.sina.com/', 'http://ent.sina.com/', 'http://tech.sina.com/', 'http://sports.sina.com/', 'http://finance.sina.com/', 'http://auto.sina.com/']
['国内', '国际', '军事', '图片', '社会', '娱乐', '科技', '体育', '财经', '汽车']

在这里插入图片描述

■ 4. 示例二:xpath

import requests
from lxml import etree

class MaoyanSpider(object):
    def __init__(self):
        self.url = 'https://maoyan.com/board/4'
        self.headers = { 'User-Agent':'' }

    def save_html(self):
        html = requests.get(url=self.url,headers=self.headers).text
        # 解析
        parse_html = etree.HTML(html)
        # 基准xpath,大的节点对象列表
        dd_list = parse_html.xpath('//dl[@class="board-wrapper"]/dd')
        item = {}
        for dd in dd_list:
            item['name'] = dd.xpath('.//p[@class="name"]/a/@title')[0].strip()
            item['star'] = dd.xpath('.//p[@class="star"]/text()')[0].strip()
            item['time'] = dd.xpath('.//p[@class="releasetime"]/text()')[0].strip()
            print(item)

    def run(self):
        self.save_html()

if __name__ == '__main__':
    spider = MaoyanSpider()
    spider.run()

输出结果:
{'name': '我不是药神', 'star': '主演:徐峥,王传君,周一围', 'time': '上映时间:2018-07-05'}
{'name': '肖申克的救赎', 'star': '主演:蒂姆·罗宾斯,摩根·弗里曼,鲍勃·冈顿', 'time': '上映时间:1994-09-10(加拿大)'}
{'name': '海上钢琴师', 'star': '主演:蒂姆·罗斯,比尔·努恩 ,克兰伦斯·威廉姆斯三世', 'time': '上映时间:2019-11-15'}
{'name': '绿皮书', 'star': '主演:维果·莫腾森,马赫沙拉·阿里,琳达·卡德里尼', 'time': '上映时间:2019-03-01'}
{'name': '霸王别姬', 'star': '主演:张国荣,张丰毅,巩俐', 'time': '上映时间:1993-07-26'}
{'name': '美丽人生', 'star': '主演:罗伯托·贝尼尼,朱斯蒂诺·杜拉诺,赛尔乔·比尼·布斯特里克', 'time': '上映时间:2020-01-03'}
{'name': '这个杀手不太冷', 'star': '主演:让·雷诺,加里·奥德曼,娜塔莉·波特曼', 'time': '上映时间:1994-09-14(法国)'}
{'name': '星际穿越', 'star': '主演:马修·麦康纳,安妮·海瑟薇,杰西卡·查斯坦', 'time': '上映时间:2014-11-12'}
{'name': '小偷家族', 'star': '主演:中川雅也,安藤樱,松冈茉优', 'time': '上映时间:2018-08-03'}
{'name': '怦然心动', 'star': '主演:玛德琳·卡罗尔,卡兰·麦克奥利菲,艾丹·奎因', 'time': '上映时间:2010-07-26(美国)'}

■ 5. 链家房产数据抓取

在这里插入图片描述
在这里插入图片描述

import requests
from lxml import etree
import time
import random
from fake_useragent import UserAgent

class LianjiaSpider(object):
    def __init__(self):
        self.url = 'https://bj.lianjia.com/ershoufang/pg{}/'
        self.blag = 1

    # 随机headers
    def get_headers(self):
        agent = UserAgent().random
        headers = { 'User-Agent':agent }
        return headers

    # 请求函数
    def get_html(self,url):
        if self.blag <= 3:
            try:
                res = requests.get(
                    url,
                    headers=self.get_headers(),
                    timeout=5
                )
                html = res.content.decode()
                return html
            except Exception as e:
                print('Retry')
                self.blag += 1
                self.get_html(url)


    # 解析提取数据
    def parse_html(self,url):
        html = self.get_html(url)
        # html要么为正常内容,要么为None
        if html:
            p = etree.HTML(html)
            # 基准xpath表达式 - 30个房源节点对象列表
            h_list = p.xpath('//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]')
            for h in h_list:
                item = {}
                # 名称
                name_list = h.xpath('.//a[@data-el="region"]/text()')
                item['name'] = name_list[0] if name_list else None
                # 户型+面积+方位+精装
                info_list = h.xpath('.//div[@class="houseInfo"]/text()')  # ['三室 | 92 | ']
                if info_list:
                    L = info_list[0].split('|')
                    if len(L) == 5:
                        item['model'] = L[1].strip()
                        item['area'] = L[2].strip()
                        item['direction'] = L[3].strip()
                        item['perfect'] = L[4].strip()
                    else:
                        item['model'] = item['area'] = item['direction'] = item['perfect'] = None
                else:
                    item['model'] = item['area'] = item['direction'] = item['perfect'] = None
                # 楼层+区域+总价+单价
                floor_list = h.xpath('.//div[@class="positionInfo"]/text()')
                item['floor'] = floor_list[0].strip() if floor_list else None

                address_list = h.xpath('.//div[@class="positionInfo"]/a/text()')
                item['address'] = address_list[0].strip() if address_list else None

                total_list = h.xpath('.//div[@class="totalPrice"]/span/text()')
                item['total'] = total_list[0].strip() if total_list else None

                unit_list = h.xpath('.//div[@class="unitPrice"]/span/text()')
                item['unit'] = unit_list[0].strip() if unit_list else None

                print(item)

    # 入口函数
    def run(self):
        for i in range(1,101):
            url = self.url.format(i)
            self.parse_html(url)
            time.sleep(random.randint(1,3))
            # 每抓取1页要初始化self.blag
            self.blag = 1

if __name__ == '__main__':
    spider = LianjiaSpider()
    spider.run()

■ 6. 百度贴吧图片下载 lxml + xpath

import requests
from lxml import etree
import time
import random
from urllib import parse

class BaiduImageSpider(object):
    def __init__(self):
        self.url = 'http://tieba.baidu.com/f?'
        # IE的User-Agent返回数据最标准
        # 如果各种检查后数据下不来,考虑使用IE的User-Agent尝试
        self.headers = { 'User-Agent':'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3)' }

    # 功能函数 - 获取html
    def get_html(self,url,params=None):
        html = requests.get(
            url=url,
            params=params,
            headers=self.headers
        ).content.decode('utf-8','ignore')
        return html

    # 功能函数2 - xpath解析
    def xpath_func(self,html,xpath_bds):
        p = etree.HTML(html)
        r_list = p.xpath(xpath_bds)
        return r_list

    # 解析+提取
    def parse_html(self,one_url,params):
        one_html = self.get_html(one_url,params)
        xpath_bds = '//div[@class="t_con cleafix"]/div/div/div/a/@href'
        # t_list: ['/p/2323','/p/2323222']
        t_list = self.xpath_func(one_html,xpath_bds)
        for t in t_list:
            t_link = 'http://tieba.baidu.com' + t
            # 保存图片
            self.save_image(t_link)

    # 给定1个帖子链接,把帖子中所有图片下载到本地
    def save_image(self,t_link):
        t_html = self.get_html(t_link)
        # 图片+视频xpath,使用xpath的或: 图片xpath | 视频xpath
        xpath_bds = '//div[@class="d_post_content j_d_post_content  clearfix"]/img[@class="BDE_Image"]/@src | //div[@class="video_src_wrapper"]/embed/@data-video'
        # img_list: ['src1','src2','src3']
        img_list = self.xpath_func(t_html,xpath_bds)
        for img in img_list:
            img_html = requests.get(
                url=img,
                headers=self.headers
            ).content
            filename = img[-10:]
            with open(filename,'wb') as f:
                f.write(img_html)

            print('%s下载成功' % filename)
            time.sleep(random.uniform(0,2))

    def run(self):
        name = input('请输入贴吧名:')
        begin = int(input('请输入起始页:'))
        end = int(input('请输入终止页:'))
        for page in range(begin,end+1):
            pn = (page-1)*50
            params = {
                'kw':name,
                'pn':str(pn)
            }
            self.parse_html(self.url,params)


if __name__ == '__main__':
    spider = BaiduImageSpider()
    spider.run()

■ 代理IP地址

在这里插入图片描述
在这里插入图片描述

■ 1. 简介

在这里插入图片描述

■ 2.

■ 3.

■ Json解析模块

■ 1. json.loads(json)

把json格式的字符串转为Python数据类型
html_json = json.loads(res.text)

■ 2. json.dump(python,f,ensure_ascii=False)

把python数据类型 转为 json格式的字符串 一般让你把抓取的数据保存为json文件时使用

第1个参数: python类型的数据(字典,列表等)
第2个参数: 文件对象
第3个参数: ensure_ascii=False # 序列化时编码

■ 示例一 将集合转换成json 写入文件中

import json

item = {'name':'QQ','app_id':1}
with open('小米.json','a') as f:
	json.dump(item,f,ensure_ascii=False)  
	
结果如下:
{"name": "QQ", "app_id": 1}

■ 示例二 将列表转换成json 写入文件中

import json

item_list = []
for i in range(3):
	item = {'name':'QQ','id':i}
	item_list.append(item)
with open('xiaomi.json','a') as f:
	json.dump(item_list,f,ensure_ascii=False)   
	
结果如下:
[{"name": "QQ", "id": 0}, {"name": "QQ", "id": 1}, {"name": "QQ", "id": 2}]

■ 3. json.dumps(python) 把 python 变量类型 转为 json 类型

将item 类型转换成json类型

import json

# json.dumps()之前
item = {'name':'QQ','app_id':1}
print('before dumps',type(item)) # dict

# json.dumps之后
item = json.dumps(item)
print('after dumps',type(item)) # str

输出结果:
before dumps <class 'dict'>
after dumps <class 'str'>

■ 4. json.load(f) 将json文件读取,并转为python类型

import json
with open('D:\\spider_test\\xiaomi.json','r') as f:
data = json.load(f)
print(data)

■ selenium+phantomjs/Chrome/Firefox

■ 1. 特点

1、简单,无需去详细抓取分析网络数据包,使用真实浏览器
2、需要等待页面元素加载,需要时间,效率低

■ 2. 安装

1、下载、解压
2、添加到系统环境变量
# windows: 拷贝到Python安装目录的Scripts目录中
# Linux : 拷贝到/usr/bin目录中
3、Linux中修改权限
# sudo -i
# cd /usr/bin/
# chmod +x phantomjs
改权限前: rwxr--r--
改权限后: rwxr-xr-x

■ 3. 使用流程

from selenium import webdriver
# 1、创建浏览器对象
browser = webdriver.Firefox(executable_path='/xxx/geckodriver')
# 2、输入网址
browser.get('URL')
# 3、查找节点
brower.find_xxxx
# 4、做对应操作
element.send_keys('')
element.click()
# 5、关闭浏览器
browser.quit()

■ 4 重要知识点

1、browser.page_source
2、browser.page_source.find('')
3、node.send_keys('')
4、node.click()
5、find_element AND find_elements
6、browser.execute_script('javascript')
7、browser.quit()

■ 5. chromedriver设置无界面模式

from selenium import webdriver
options = webdriver.ChromeOptions()
# 添加无界面参数
options.add_argument('--headless')
browser = webdriver.Chrome(options=options)
browser.get('http://www.baidu.com/')
browser.save_screenshot('baidu.png')

■ 6. Selenium- Web自动化测试工具

■ 6. 1 定义

Web自动化测试工具,可运行在浏览器,根据指令操作浏览器
只是工具,必须与第三方浏览器结合使用

■ 6. 2 安装

Linux: sudo pip3 install selenium
Windows: python -m pip install selenium

■ 6. 3 selenium - 键盘操作

from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/')
# 1、在搜索框中输入"selenium"
browser.find_element_by_id('kw').send_keys('赵丽颖')
# 2、输入空格
browser.find_element_by_id('kw').send_keys(Keys.SPACE)
# 3、Ctrl+a 模拟全选
browser.find_element_by_id('kw').send_keys(Keys.CONTROL, 'a')
# 4、Ctrl+c 模拟复制
browser.find_element_by_id('kw').send_keys(Keys.CONTROL, 'c')
# 5、Ctrl+v 模拟粘贴
browser.find_element_by_id('kw').send_keys(Keys.CONTROL, 'v')
# 6、输入回车,代替 搜索 按钮
browser.find_element_by_id('kw').send_keys(Keys.ENTER)

■ 6. 4 selenium - 鼠标操作

from selenium import webdriver
# 导入鼠标事件类
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
driver.get('http://www.baidu.com/')
#移动到 设置,perform()是真正执行操作,必须有
element = driver.find_element_by_xpath('//*[@id="u1"]/a[8]')
ActionChains(driver).move_to_element(element).perform()
#单击,弹出的Ajax元素,根据链接节点的文本内容查找
driver.find_element_by_link_text('高级搜索').click()

■ 6. 5 selenium - 切换页面

页面中点开链接出现新的页面,但是浏览器对象browser还是之前页面的对象

# 获取当前所有句柄(窗口)
all_handles = browser.window_handles
# 切换browser到新的窗口,获取新窗口的对象
browser.switch_to.window(all_handles[1])

■ 6. 6 selenium - iframe子框架

网页中嵌套了网页,先切换到iframe子框架,然后再执行其他操作
browser.switch_to.iframe(iframe_element)

■ 6. 6.1 示例 - 登录qq邮箱
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get('https://mail.qq.com/')

# 切换到iframe子框架
login_frame = driver.find_element_by_id('login_frame')
driver.switch_to.frame(login_frame)

# 用户名+密码+登录
driver.find_element_by_id('u').send_keys('qq账号')
driver.find_element_by_id('p').send_keys('qq密码')
driver.find_element_by_id('login_button').click()

■ 7. phantomjs 无界面浏览器

定义
无界面浏览器(又称无头浏览器),在内存中进行页面加载,高效

Windows安装(phantomjs、chromedriver、geckodriver)

■ 8. phantomjs、chromedriver、geckodrive

■ 8.1 Windows安装

1、下载对应版本的phantomjs、chromedriver、geckodriver
2、把chromedriver.exe拷贝到python安装目录的Scripts目录下(添加到系统环境变量)
# 查看python安装路径: where python
3、验证
cmd命令行: chromedriver
# 下载地址
1、chromedriver : 下载对应版本
http://npm.taobao.org/mirrors/chromedriver/   失效
[添加链接描述](https://developer.chrome.com/docs/chromedriver/downloads?hl=zh-cn)  
[下载链接描述](https://googlechromelabs.github.io/chrome-for-testing/)
2、geckodriver
https://github.com/mozilla/geckodriver/releases
3、phantomjs
https://phantomjs.org/download.html

■ 8.2 Linux 安装

1、下载后解压
tar -zxvf geckodriver.tar.gz
2、拷贝解压后文件到 /usr/bin/ (添加环境变量)
sudo cp geckodriver /usr/bin/
3、更改权限
sudo -i
cd /usr/bin/
chmod 777 geckodriver

■ 8. 示例代码一:使用 selenium+浏览器 打开百度

# 导入seleinum的webdriver接口
from selenium import webdriver
import time

# 创建浏览器对象
browser = webdriver.PhantomJS()
browser.get('http://www.baidu.com/')

time.sleep(5)

# 关闭浏览器
browser.quit()

■ 9. 示例代码二:打开百度,搜索赵丽颖,点击搜索,查看

from selenium import webdriver
import time

# 1.创建浏览器对象 - 已经打开了浏览器
browser = webdriver.Chrome()
# 2.输入: http://www.baidu.com/
browser.get('http://www.baidu.com/')
# 3.找到搜索框,向这个节点发送文字: 赵丽颖
browser.find_element_by_xpath('//*[@id="kw"]').send_keys('赵丽颖')
# 4.找到 百度一下 按钮,点击一下
browser.find_element_by_xpath('//*[@id="su"]').click()

■ 10 浏览器对象(browser)方法

# from selenium import webdriver
browser = webdriver.Chrome(executable_path='path')
browser.get(url)
browser.page_source # HTML结构源码
browser.page_source.find('字符串')
# 从html源码中搜索指定字符串,没有找到返回:-1
browser.quit() # 关闭浏览器

■ 11. 定位节点

■ 11.1 单元素查找(1个节点对象)

browser.find_element_by_id('')
browser.find_element_by_name('')
browser.find_element_by_class_name('')
browser.find_element_by_xpath('')

■ 11.2 多元素查找([节点对象列表])

browser.find_elements_by_id('')
browser.find_elements_by_name('')
browser.find_elements_by_class_name('')
browser.find_elements_by_xpath('')

■ 11.3 节点对象操作

ele.send_keys('') # 搜索框发送内容
ele.click()
ele.text # 获取文本内容,包含子节点和后代节点的文本内容
ele.get_attribute('src') # 获取属性值

■ 11.3 京东爬虫案例

在这里插入图片描述
执行JS脚本,获取动态加载数据

browser.execute_script(
	'window.scrollTo(0,document.body.scrollHeight)'
)
from selenium import webdriver
import time
import pymongo

class JdSpider(object):
    def __init__(self):
        self.url = 'https://www.jd.com/'
        # 设置无界面模式
        self.options = webdriver.ChromeOptions()
        self.options.add_argument('--headless')
        self.browser = webdriver.Chrome(options=self.options)
        # 计数
        self.i = 0
        # 3个对象
        self.conn = pymongo.MongoClient(
            '127.0.0.1',27017
        )
        self.db = self.conn['jddb']
        self.myset = self.db['jdset']

    # 输入地址+输入商品+点击按钮
    def get_html(self):
        self.browser.get(self.url)
        self.browser.find_element_by_xpath('//*[@id="key"]').send_keys('爬虫书')
        self.browser.find_element_by_xpath('//*[@id="search"]/div/div[2]/button').click()
        time.sleep(2)


    # 把进度条拉到最底部+提取商品信息
    def get_data(self):
        # 拉动进度条
        self.browser.execute_script(
            'window.scrollTo(0,document.body.scrollHeight)'
        )
        # 给页面元素加载预留时间
        time.sleep(3)

        # 提取数据
        li_list = self.browser.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li')

        for li in li_list:
            item = {}
            item['name'] = li.find_element_by_xpath('.//div[@class="p-name p-name-type-2"]/a/em').text.strip()
            item['price'] = li.find_element_by_xpath('.//div[@class="p-price"]').text.strip()
            item['commit'] = li.find_element_by_xpath('.//div[@class="p-commit"]/strong').text.strip()
            item['shop'] = li.find_element_by_xpath('.//div[@class="p-shop"]').text.strip()
            print(item)
            # 存入mongodb数据库
            self.myset.insert_one(item)
            self.i += 1

    def run(self):
        # 此方法一定只执行一次
        self.get_html()
        while True:
            self.get_data()
            # class: pn-next
            # class: pn-next disabled
            if self.browser.page_source.find('pn-next disabled') == -1:
                self.browser.find_element_by_class_name('pn-next').click()
                # 给元素加载预留时间
                time.sleep(1)
            else:
                print('数量:',self.i)
                break

if __name__ == '__main__':
    spider = JdSpider()
    spider.run()

■ Scrapy框架

在这里插入图片描述

■ 1 Scrapy-定义

异步处理框架,可配置和可扩展程度非常高,Python中使用最广泛的爬虫框架

■ 2 Scrapy-安装

# Ubuntu安装
1、安装依赖包
	1、sudo apt-get install libffi-dev
	2、sudo apt-get install libssl-dev
	3、sudo apt-get install libxml2-dev
	4、sudo apt-get install python3-dev
	5、sudo apt-get install libxslt1-dev
	6、sudo apt-get install zlib1g-dev
	7、sudo pip3 install -I -U service_identity
2、安装scrapy框架
	1、sudo pip3 install Scrapy
	
# Windows安装
cmd命令行(管理员): python -m pip install Scrapy
# Error: Microsoft Visual C++ 14.0 is required xxx

■ 3 Scrapy-框架五大组件

1、引擎(Engine) :整个框架核心
2、调度器(Scheduler) :维护请求队列
3、下载器(Downloader):获取响应对象
4、爬虫文件(Spider) :数据解析提取
5、项目管道(Pipeline):数据入库处理
**********************************
# 下载器中间件(Downloader Middlewares) : 引擎->下载器,包装请求(随机代理等)
# 蜘蛛中间件(Spider Middlewares) : 引擎->爬虫文件,可修改响应对象属性

■ 4 scrapy-爬虫工作流程

# 爬虫项目启动
1、由引擎向爬虫程序索要第一个要爬取的URL,交给调度器去入队列
2、调度器处理请求后出队列,通过下载器中间件交给下载器去下载
3、下载器得到响应对象后,通过蜘蛛中间件交给爬虫程序
4、爬虫程序进行数据提取:
	1、数据交给管道文件去入库处理
	2、对于需要继续跟进的URL,再次交给调度器入队列,依次循环

■ 5 scrapy-常用命令

# 1、创建爬虫项目
scrapy startproject 项目名
# 2、创建爬虫文件
scrapy genspider 爬虫名 域名
# 3、运行爬虫
scrapy crawl 爬虫名

■ 6 scrapy-项目目录结构

Baidu # 项目文件夹
├── Baidu # 项目目录
│ ├── items.py # 定义数据结构
│ ├── middlewares.py # 中间件
│ ├── pipelines.py # 数据处理
│ ├── settings.py # 全局配置
│ └── spiders
│ ├── baidu.py # 爬虫文件
└──
scrapy.cfg # 项目基本配置文件

■ 7 scrapy-全局配置文件settings.py详解

# 1、定义User-Agent
USER_AGENT = 'Mozilla/5.0'
# 2、是否遵循robots协议,一般设置为False
ROBOTSTXT_OBEY = False
# 3、最大并发量,默认为16
CONCURRENT_REQUESTS = 32
# 4、下载延迟时间
DOWNLOAD_DELAY = 1
# 5、请求头,此处也可以添加User-Agent
DEFAULT_REQUEST_HEADERS={}
# 6、项目管道
ITEM_PIPELINES={
	'项目目录名.pipelines.类名':300
}

■ 8 scrapy-创建爬虫项目步骤

1、新建项目 :scrapy startproject 项目名
2、cd 项目文件夹
3、新建爬虫文件 :scrapy genspider 文件名 域名
4、明确目标(items.py)
5、写爬虫程序(文件名.py)
6、管道文件(pipelines.py)
7、全局配置(settings.py)
8、运行爬虫 :scrapy crawl 爬虫名

■ 9 scrapy-pycharm运行爬虫项目

1、创建begin.py(和scrapy.cfg文件同目录)
2、begin.py中内容:
	from scrapy import cmdline
	cmdline.execute('scrapy crawl maoyan'.split())

■ 10 示例一:打开百度首页,把 ‘百度一下,你就知道’ 抓取下来,从终端输出

■ 10.1 创建项目Baidu 和 爬虫文件baidu

1、scrapy startproject Baidu
2、cd Baidu
3、scrapy genspider baidu www.baidu.com

■ 10.2 编写爬虫文件baidu.py,xpath提取数据

# -*- coding: utf-8 -*-

import scrapy
class BaiduSpider(scrapy.Spider):
	name = 'baidu'
	allowed_domains = ['www.baidu.com']
	start_urls = ['http://www.baidu.com/']
	def parse(self, response):
		result = response.xpath('/html/head/title/text()').extract_first()
		print('*'*50)
		print(result)
		print('*'*50)

■ 10.3 全局配置settings.py

USER_AGENT = 'Mozilla/5.0'
ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
	'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
	'Accept-Language': 'en',
}

■ 10.4 创建run.py(和scrapy.cfg同目录)

from scrapy import cmdline

cmdline.execute('scrapy crawl baidu'.split())

■ 10.5 启动爬虫

直接运行 run.py 文件即可

■ 11 示例二:

■ 12 示例三:

■ 中间件

■ 1.

■ 2.

■ 3.

■ 多线程爬虫

■ 1. 队列

# 导入模块
from queue import Queue
# 使用
q = Queue()
q.put(url)
q.get() # 当队列为空时,阻塞
q.empty() # 判断队列是否为空,True/False

■ 2. 线程模块

# 导入模块
from threading import Thread

# 使用流程
t = Thread(target=函数名) # 创建线程对象
t.start() # 创建并启动线程
t.join() # 阻塞等待回收线程

# 如何创建多线程
t_list = []

for i in range(5):
	t = Thread(target=函数名)
	t_list.append(t)
	t.start()
for t in t_list:

t.join()

■ 3. 线程锁

# 注意多线程写入的线程锁问题
from threading import Lock
lock = Lock()
# 加锁
lock.acquire()
python语句
# 释放锁
lock.release()

■ 4. 小米应用商店抓取(多线程)

import requests
import json
from threading import Thread,Lock
from queue import Queue
import time
import csv
import re

class XiaomiSpider(object):
    def __init__(self):
        self.url = 'http://app.mi.com/categotyAllListApi?page={}&categoryId={}&pageSize=30'
        self.headers = {'User-Agent':''}
        # 创建url队列,存放所有待爬取的URL地址
        self.q = Queue()
        self.f = open('xiaomi.csv','a')
        self.lock = Lock()
        self.writer = csv.writer(self.f)
        # 计数
        self.i = 1

    # 获取所有类别及code
    def get_all_type(self):
        url = 'http://app.mi.com/'
        html = requests.get(url=url,headers=self.headers).text
        p = re.compile('<a href="/category/(.*?)">(.*?)</a>',re.S)
        # r_list: [(2,'聊天社交'),(),()]
        r_list = p.findall(html)
        for r in r_list:
            # 遍历一个类别,就要把这个类别的所有页put到队列中
            self.url_in(r)

    # 总页数
    def get_total(self,code):
        url = self.url.format(0,code)
        html = requests.get(url=url,headers=self.headers).json()
        count = html['count']
        if count % 30 == 0:
            total = count // 30
        else:
            total = (count // 30) + 1

        return total

    # url入队列
    def url_in(self,r):
        # 获取此类别总页数
        total = self.get_total(r[0])
        for page in range(total):
            url = self.url.format(page,r[0])
            self.q.put(url)

    # 线程事件函数
    def parse_html(self):
        while True:
            if not self.q.empty():
                url = self.q.get()
                html = requests.get(url=url,headers=self.headers).json()
                app_list = []
                for app in html['data']:
                    name = app['displayName']
                    app_list.append((name,))
                    print(name)
                    self.lock.acquire()
                    self.i += 1
                    self.lock.release()

                # 加锁+释放锁
                self.lock.acquire()
                self.writer.writerows(app_list)
                self.lock.release()
                time.sleep(0.1)
            else:
                break

    # 入口函数
    def run(self):
        # url地址入队列
        self.get_all_type()

        t_list = []
        for i in range(5):
            t = Thread(target=self.parse_html)
            t_list.append(t)
            t.start()

        for t in t_list:
            t.join()

if __name__ == '__main__':
    start = time.time()
    spider = XiaomiSpider()
    spider.run()
    end = time.time()
    print('数量:',spider.i)
    print('执行时间:%.2f' % (end-start))

■ 5. 多级页面多线程抓取- 腾讯招聘数据抓取

在这里插入图片描述

import requests
import json
import time
from urllib import parse

class TencentSpider(object):
    def __init__(self):
        self.one_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1571206111698&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword={}&pageIndex={}&pageSize=10&language=zh-cn&area=cn'
        self.two_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1571206506767&postId={}&language=zh-cn'
        self.headers = {'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
        # 打开文件
        self.f = open('tencent.json','w')
        self.job_list = []

    def parse_html(self,url):
        one_html = requests.get(
            url=url,
            headers=self.headers
        ).json()
        # 提取postId,拼接二级页面地址
        for one in one_html['Data']['Posts']:
            # postId ,two_url
            postid = one['PostId']
            two_url = self.two_url.format(postid)
            self.get_data(two_url)
            time.sleep(0.1)

    def get_data(self,two_url):
        html = requests.get(
            url=two_url,
            headers=self.headers
        ).json()
        job = {}
        job['name'] = html['Data']['RecruitPostName']
        job['duty'] = html['Data']['Responsibility']
        job['require'] = html['Data']['Requirement']
        # 所有数据都添加都字典job中
        self.job_list.append(job)
        print(job)

    def run(self):
        keyword = input('请输入关键字:')
        keyword = parse.quote(keyword)

        total_page = self.get_total_page(keyword)
        for page in range(1,total_page+1):
            url = self.one_url.format(keyword,page)
            self.parse_html(url)

        # 存储到json文件中
        json.dump(self.job_list,self.f,ensure_ascii=False)
        # 关闭文件
        self.f.close()

    # 获取总页数
    def get_total_page(self,keyword):
        url = self.one_url.format(keyword,1)
        html = requests.get(url=url,headers=self.headers).json()
        count = html['Data']['Count']
        if count % 10 == 0:
            total_page = count // 10
        else:
            total_page = (count // 10) + 1

        return total_page

if __name__ == '__main__':
    spider = TencentSpider()
    spider.run()

■ 机器视觉

■ 1.

■ 2.

■ 3.

■ 极限滑块验证码破解

■ 1.

■ 2.

■ 3.

■ 移动端数据抓取

■ 1.

■ 2.

■ 3.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光芒Shine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值