青魔法-驭虫术(不定时更新)

青魔法Python-驭虫术

本文作者:魏泯

博客源地址:https://www.cnblogs.com/simple/

做有效率的魔法师

*跳转到文章结尾*


目录

  • 概念、工具以及协议
  • 使用requests模块开发爬虫程序
    • rquests请求
    • requests的其他参数
    • response响应对象
  • 处理cookie相关的请求
  • Scrapy框架
  • 第三方模块
  • 微型ORM(对象关系映射)脚本【源代码】

概念、工具 以及 协议

爬虫

爬虫就是 模拟浏览器发送网络请求,获取响应,按照规则提取数据的程序。爬虫要的就是保存数据,只抓取不保存毫无用处。

数据流向

  • 呈现出来,用于网页服务,或应用服务
  • 进行保存、数据分析

需要的软件与环境

  • python环境
  • Pycharm
  • chrome浏览器

浏览器的请求

Url:需要进行解码或转码
爬虫和浏览器请求的URL地址有一定差别。Elements与Network中的源码会不一样。

HTTP与HTTPS

Http:超文本传输协议:以明文方式进行传输,效率更高。
Https:Http+SSL(安全套接字层):传输数据之前,将数据加密,之后解密,效率较低,但是更安全。

这两者都包含请求与响应部分。

请求

Http报文请求由三部分组成:请求行、请求头、请求体

  • 请求头:

Status code:状态码
Conection: keep-alive 支持长连接
Cache-Control:max-age=0 缓存控制为零
User-Agent:浏览器(内核)身份标识
Upgrade-Insecure-Requests:支持将不安全请求转换的功能
Accept:表示我想接收什么数据,q=权重
Cookie:浏览器保存在用户本地的一些信息,包括用户密码,账号信息,网站校验信息(例如csrf跨站保护)。会被携带发送给服务器。Cookie可以被服务器用来判断客户端是否是爬虫。
Query-String-Parameters:请求数据(GET)
From-data:请求数据(POST)
可以在Chorme中模拟多种版本的机器

  • 请求体

GET请求没有请求体,数据放在URL中
POST请求有请求体:携带数据,参数放置于请求体中,常用于登陆注册、传输大文本。

响应

  • 响应头: set-Cookie——服务器给本地设置的cookie信息,服务器通过该字段设置cookie
  • 响应体: URL地址对应的响应
  • 抓包:就是按下F12,在Network中查看浏览器发送了什么请求的信息,这个过程就叫做抓包

*返回目录再看看*


使用requests模块开发爬虫程序

requests的方法

requests模块是一个第三方包:用于模拟http数据请求。
在接下来的内容里我们学习一下它的多种请求。
requests方式比urlib方式简单
50%的动态内容是json格式的数据。

requests模块的请求方法

- requests请求的get请求方式

Http请求的get的请求方式:

使用这种方式得到一个响应对象

url = "http://www.baidu.com/"
response = requests.get(url=url)

Terminal:

<Response [200]>
  • requests请求的post请求方式

使用这种方式得到一个响应对象

url = "http://www.baidu.com/"
data = {键:"值"}
response = requests.get(url=url,data=data)

Terminal:

<Response [200]>

请求头部信息:

- 伪造/重塑 请求头部信息:

headers是一个Hash字典,以键值的方式存放头部信息。
例如:

headers={"键":"值,}
response = requests.get(url=url, headers=headers)

requests的其他参数

除了url和以外还有很多参数,比如timeout、代理IP等等

requests请求的超时参数——timeout

配合异常捕获,在指定时间返回响应

# 在三秒内必须返回响应,否则则会报错
response = requests.get(url=url,headers=headers,timeout=3)

建立代理

  • 建立代理
proxies = [{"http":"113.122.8.45:53128"},{"http":"114.122.8.45:53128"},{"http":"115.122.8.45:53128"}]
  • 导入随机数模块
proxy = random.choice(proxies)

response的方法:

通过response获取页面的响应内容,包括响应头、响应体。

这是我们所使用的公共代码:

import requests
url = 'https://www.cnblogs.com/Asterism-2012/'
response = requests.get(url=url)

获取源代码

  • response.content
    第一种情况,使用content方法获取源代码,但是这种方式是以byte的方式获取源代码,先看一下它是什么,看完这个我们稍后对它解码
print(response.content)

Terminal:

b'\r\n<!DOCTYPE html>\r\n<html lang="zh-cn">\r\n<head>\r\n<meta ......略
  • response.content.code()

我们发现刚才打印的content内容是byte,为了便于观看,进行解码:
它会将content的内容解码为str类型,也可以自行指定解码类型。

x = response.content.decode()
print(type(x))
print(x)

Terminal:

<class 'str'>
略......<p>by: 魏泯</p><!--PageEndHtml Block End--></body></html>

decode()自行指定content解码类型:

x= response.content.decode('utf-8')
print(type(x))

Terminal:

<class 'str'>
  • response.text

使用text方法获取到的源代码,是根据HTTP 头部对响应的编码作出有根据的推测,推测的文本编码。这种方法简单常用,适用于大部分的网站。当然既然是推测的代码,就也有出现错误的时候,这也没关系,到时候我们可以通过指定编码来解决这个问题。

print(response.text)

Terminal:

略......<p>by: 魏泯</p><!--PageEndHtml Block End--></body></html>

设置服务器的编码为本地的编码

但是有的时候即便进行了解码,也无法正常看到页面源码。造成这样的原因是本地编码与服务器编码不统一,所以我们要对响应回来的内容制定编码。
  • 第一种方式,自动获取:
response.encoding = response.apparent_encoding
  • 第二种方式,手动抓包设置,假设以抓到的是gbk为例:
response.encoding=”gbk”

请求与响应的目标url地址

  • 请求目标站点的Url:

有时候我们需要获取到目标站点的url,用于拼接新的url或者做保存、跳转等工作,所以就用到了response.request.url
这个方法

x = response.request.url

Terminal:

https://www.cnblogs.com/Asterism-2012/
  • 实际发生响应的Url地址:

响应的url地址,有时候会因为跳转而和请求目标地址不一致,使用response.url方法来获取它:

x = response.url
print(x)

Terminal:

https://www.cnblogs.com/Asterism-2012/

请求与响应的头部信息

  • 查看请求头部信息:

有时候我们需要测试自己的代理IP池、Header池信息,就可以通过这个方法来动态获取头部信息进行查看

x = response.request.headers
print(x)

Terminal:

{'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
  • 查看响应头部信息:
x = response.headers
print(x)

Terminal:

{'Date': 'Fri, 04 Jan 2019 03:53:18 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'Cache-Control': 'private, max-age=10', 'Expires': 'Fri, 04 Jan 2019 03:53:28 GMT', 'Last-Modified': 'Fri, 04 Jan 2019 03:53:18 GMT', 'X-UA-Compatible': 'IE=10', 'X-Frame-Options': 'SAMEORIGIN', 'Content-Encoding': 'gzip'}

处理cookie相关的请求

  • DEMO:人人网
  • 直接携带cookie,然后请求URL
    • 直接放在headers中
    headers = {"Referver":"URL","User-Agent":"浏览器信息","Cookie":"..."}
    
    • cookie字典传给cookie参数,这个办法非常酷。Cookie参数只接收字典。【传说中的字典推导式】
    # Cookie字符串
    CookieStr = "anonymid=jm5lz4q0wv29sz; depovince=BJ; _r01_=1; JSESSIONID=abcpoYHrUXJZa6yQOiLxw; ick_login=4c5c0781-d37e-44c5-98a7-8e3fee81d222; ick=e74b83d4-c258-4de9-bd6c-3426f777fadd; XNESSESSIONID=949847ff1bf9; jebe_key=38dc9339-f175-472f-9288-02e4e7e4b59c%7Cd931901a60720bfd1a4f2f38c98639f3%7C1537147847212%7C1%7C1537147849534; wp_fold=0; jebecookies=054ec649-ffed-4989-beda-1be907785a33|||||; _de=BA25AEE63DF6B7A91E89E9A775AD2FBD; p=5929761e2707b622b09b23f92b3c04aa8; first_login_flag=1; ln_uact=15661592012; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; t=aaf1d9103cf916e1d824260c832ceaf38; societyguester=aaf1d9103cf916e1d824260c832ceaf38; id=968054908; xnsid=1acd462c; ver=7.0; loginfrom=null"
    
    # 这是一个字典生成式,生成正式使用的Cookie
    Cookie = {i.split("=")[0]:i.split("=")[1] for i in CookieStr.split(";")}
    response = requests.get(url=url,headers=headers,cookies=Cookie)
    
  • 通过session模块获取cookie:(这应是最常用的方法)
    1. session = requests.session() 会话保持,实例session
    2. session.post(post_url,data,headers) 很多网页的接口可以在表单的action提交地址中看到,但有一些需要抓包才能看到
    3. response = session.get(url,headers)
    • 使用session不仅可以处理相关登陆,而且可以应对相关反爬措施。
  • 处理登陆相关的验证码可以找:打码兔

*返回目录再看看*


数据提取方法

JSON

  • 数据交换格式,在Python的识别中其实是一个字符串
  • 学习处理和操纵json数据,非常重要
  • 导入标准库模块jsonimport json
  • json.loads(JSON数据) Json字符串重载为Python可识别的字典(dict)类型
    • 将Json字符串转化为Python容器类型(Dic,List)
    Py_data = json.loads(response.text)`
    
  • json.dumps(dict) 字典转换成json字符串
    • 把Python数据格式转化成字符串
    Js_data = json.dumps(Py_data)
    
    • dumps参数;ensure_ascii=False 取消ASC编码
    Js_data = json.dumps(Py_data,ensure_ascii=False)
    
    • dumps参数;indent=2 以首行缩进两个空格 的形式打印或保存,美化效果
    Js_data = json.dumps(Py_data,indent=2)
    
  • 浏览器的手机模式
  • 抓包:
    • 过滤:可以通过过滤URL来大幅提高抓包效率
    • Preview:中文不会被编码,会直接正常显示
  • API的获取注意:动态数据无法正常显示,很可能是Referer导致的

Xpath与lxml

Xpath是一种提取数据的语言

  • 当使用Xpath-helper工具 定位到任何一个节点,该节点都会得到一个名为xh-highlight的class属性做标识
  • XPath-helper插件
  • 解析网页内容:静态内容(源代码中有的)。
  • ctrl+shift+x 呼出XPath-helper
  • 按住shift移动光标,快速捕获响应的xpath
  • 1.选择节点对象(标签)
    • /html/head/meta:能够选中html下的head下的所有的meta标签
  • 2.从任意节点开始选择
    • //li:当前页面所有的li标签
    • /html/head/link//li:表示meta下的所有li标签
  • 3.定位到具体元素 [@]
    • //div[@class="father"]//li: 获取class名为father的div下的所有li标签
    • 可以使用单引号
    • 在页面中,定位id往往比定位class更合适
  • 4.获取节点内文本 text()
    • //a/text():获取a标签中直接裸露的文本内容
    • a//text():获取a下的所有文本,包括子节点文本
  • 5.选择节点属性内容
    • a/@href:获取a标签中 href属性中的内容

xpath方法

  • string()函数 提取多个子节点中的文本
# 获取词语解释
w_ex = html2.xpath('//*[@id="content"]/div[2]')[0].xpath("string(.)")

lxml模块

  • 第三方模块,解析xml内容

  • etree.HTML方法,将响应的代码格式化,解析成标准的html格式。

  • @后面跟属性,如果放在方括号中,代表对所过滤的标签集合进行过滤。如果放在url后,则代表获取该属性的值

    etree.HTML(response.text)

相对路径与绝对路径

  • /是绝对路径,绝对路径的查找难度比较大,比较麻烦
  • //是相对路径,一般情况下我们都使用相对路径进行查找

mango数据库或文件夹

  • 保存爬取内容
  • 在xpath中的下标切片,不支持负索引,而且是从一开始,而不是从零开始的。
  • 把tbody去掉就好用了

JSON-View插件

  • 更便于查看JSON数据,在原来的基础上,优化了JSON数据的显示格式

response

  • text方法,响应的源代码,字符串类型;可以提取汉字。
  • encoding编码
# 将响应信息编码改为网站制定编码
response.encoding = response.apparent_encoding

文件操作

  • os模块,用于操作系统文件
  • 判断文件夹是否存在
dir = "directory"
if not os.path.exsits(dir):
pass
  • 创建文件夹
dir = "directory"
os.mkdir(dir)
  • 打开文件与文件读写
with("file_name","w+",encoding="utf-8") as f:
    f.write(content)

爬取动态内容与基础反爬

  • 网页是响应式加载,而不是一次性全部自动加载的
  • Grome浏览器F12:
    • Network中可以查看请求信息
    • Network-XHR 查看动态请求信息
  • user-agent/eɪdʒənt/:显示请求浏览器的内核
# 获取返回响应信息头部,这个不是很重要
for v,j in response.headers.items():
    print(v,':',j)
# 获取请求信息头部
for v,j response.request.headers.items():
    print(v,':',j)
  • 伪造请求信息浏览器内核
headers = {
'User-Agent':'伪造的浏览器内核信息'
}
response = requests.get(url=url,headers=headers)
  • Referer溯源伪造
    有些网址的反爬策略是:某些网址的特定网页会识别客户端的refer信息,这些信息包含了客户端的请求记录。它的具体意思是,你必须经由访问了我的主页,跳转到我的子页面,而不是从URL直接过来。你先开我家门,才能到我的卧室,而不是从窗户进来。否则就认为你是不合法的。
headers = {
'Referer':'伪造的 上一级URL'
}
response = requests.get(url=url,headers=headers)
  • 在XHR中可以找到动态数据,以及动态数据的请求接口。
  • 将json数据格式,格式化
py_date = json.loads(response.text())

IP代理 requests.proxies

  • 相关则是随机数模块 random
# 代理IP列表
proxies = [{"http":“133.12.45.12:8564”},{"协议":"IP:端口号"}]
# random随机选择列表中的一个元素生成代理
proxy = random。choice(proxies)
response = requests.get(proxies=proxy)

MongoDB 数据库

  • 基于分布式数据库,非关系性数据库中最接近关系型数据库。
  • 库→集合→文档
  • 服务器端开启服务
mongod.exe --dbpath C:\data\db(路径)
指定服务地址
  • 开启客户端
mongod(在环境变量下)
  • 创建数据库
use DB_NAME
  • 插入数据(创建集合)
db.tb_name.insert({key:value})
  • 查看数据库
show dbs;
  • 删除数据库
db.dropDatabase()
  • 创建集合
db.createCollection("set_name")
  • 查看集合
show collections
  • 删除集合
db.collection.drop()

Scrapy框架

  • 建立爬虫的命令方式

scrapy genspider x www.x.com
这里使用的版本是1.0以上的版本,在填写爬虫域名的时候 不需要加引号

  • 运行爬虫
    scrapy crawl x
  • 扩展:
    • 查看爬虫项目的模式列表
    >>>scrapy genspider -l 
    Available templates:
    basic
    crawl
    csvfeed
    xmlfeed
    
    • 使用上述模式中的一种,指定它,使用它进行开发(构建)爬虫文件。

spider.py文件:

爬虫文件是整个框架中我们最常编写的,里面包括一些内容需要我们学习。

extract()函数

  • 代替etree将格式化后的源码内容输出

parse()函数

  • parse() 是spider的一个方法。
  • 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。
  • 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。

setting.py文件:

这里的配置有很多,但并不是我们所学的全部,譬如分布式设定,是要自己手写进去的

  • robots.txt的规则遵循设定:这里设置是否遵循robots文件的协定,一般情况下设置成False就行
# Obey robots.txt rules
ROBOTSTXT_OBEY = False      # default:True
  • 设置全局Header头:在settings.py文件中搜索就可以找到,取消注释之后可以将headers头信息加入进去。(在DownloadMiddlewares【下载中间件】中也可以加headers请求头信息,cookie,代理等。)
# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}
  • 设置爬取间隔:在制定范围内随机的秒数作为爬取间隔,下面这个五表示的是最大爬取间隔时间(框架会从中1~5秒中选取一个时间点),并不是每次爬取都使用这个数字作为间隔秒数。

# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
  • 开启入库入口文件piplines.py的使用:在setting.py中可以找到这些代码,将它解开,就可以使用。同时还可以在ITEM_PIPELINES字典中设置新的内容(这些内容就是piplines.py中新的类),并且给它设置不同的优先级。指数越低,优先级越高。
#ITEM_PIPELINES = {
#    'Csdnboke.pipelines.CsdnbokePipeline': 300,
#}

piplines.py文件:

一种去重方法

  • 首先建立一个新的,使用逻辑实现去重,源代码如下:
# 导入模块
from scrapy.exceptions import DropItem
# 建立一个用于去重的类
class Set_it(object):
    # 建立一个构造方法
    def __init__(self):
        self.set0 = set()
    # 建立一个process_item方法,它会自动调用
    def process_item(self, item, spider):
        # 标题
        title = item["title"]
        # 判断标题是否已经存入了set中
        if title in self.set0:
            # 将这条item记录删除,并且输出一条提示信息
            raise DropItem("这一条内容已经存在了,所以我把它删了:%s"%title)
        # 否则将标题存入
        else:
            # 
            self.set0.add(title)
        return item

第三方模块

newspaper(第三方爬虫包)

> newspaper是一个第三方包,用于快速抓取页面信息。它的开发团队来自于美国,现在还仍在维护当中。如果要使用,首先要保证在你的电脑上下载并安装这个包,对此,我们可以使用pip这样的包管理器进行直接下载安装,当然,用conda也可以。
  • 安装newspaper
pip install newspaper3k
  • 在Python爬虫文件中导入它
import newspaper
  • 使用实例化一个newspaper的Article方法,这个方法接收两个参数,第一个参数是Url,第二个参数是区域语言。
# 实例化Article
# demo:
# news = newspaper.Article()
news = newspaper.Article(url=base_url,language='zh')
  • 执行Article的download方法保存源码,然后执行parse方法,这两步是目前必须的
news.download()
news.parse()
  • 获取页面正文
content = news.text
  • 获取页面标题
title = news.title
  • 获取文章内配图
img = news.imges

Retrying 模块

retrying是一个第三方模块,用于重试出现异常的函数。
首先进行安装:

pip install retrying (安装)

代码示例(“出事了算我的,你尽管去做,做错了也不怕,但最多三次”):

from retrying import retry
# 作为装饰器 装饰函数,报错仍然重复执行,直到三次才算出错
@ retry(stop_max_attempt_number=3)
def fun1():
    print("hello world.")
    time.sleep(1)
    raise ValueError("This is a test error.")

开发一个微型ORM(对象关系映射)脚本【源代码】

#coding=utf-8
'''微型ORM v1.1
@help:
首先要保证你的items中的键值与数据库中的字段吻合,其次要自己建立库和数据表,
传入你的item,并且传入表的名称
@Form: 魏泯
'''

def sql(item,table):
	'''
	item: Your dict.
	table: Your table name.
	'''
	# 生成两个列表推导式
	keylis = [key for key,values in item.items()]
	vlis = [values for key,values in item.items()]
    # 生成sql 语句中所需要的
	str1215 = '"{}"'+',"{}"'*(len(item)-1)
	str1217 = '{}'+',{}'*(len(item)-1)
	str1216 = 'insert into '+table+'('+str1217+')'+' value ('+str1215+')'
	_sql = str1216.format(*keylis,*vlis)
	return _sql      # 返回sql语句

*返回目录再看看*

posted on 2018-12-03 21:40  墨书韶光(魏泯i)  阅读(560)  评论(0编辑  收藏  举报

导航

by: 魏泯