文章目录
前言
目的:- 尝试借助CSDN构建自己的知识学习体系
- 记录自身爬虫项目的学习过程
- 保持新鲜感,不断进步
一、爬虫必备知识
- 计算机网络基础
- 前端知识
- Html解析 (eg. beautifulsoup/xpath)
- 数据存储(eg. peewee/SQLAlchemy)ORM框架
- 静态、动态页面抓取
- 模拟登陆
- 验证码问题
- 反爬技术
- 多线程和线程池
二、网络协议与爬虫重点
-
网络协议
-
重点掌握
(1) TCP/IP
(2) Socket
(3) HTTP
(4) GET/POST
(5) html/css/JavaScript
(6) request/json数据
(7) 正则表达式(python re库)
(8) beautifulsoup/xpath
三、静态页面抓取(以CSDN论坛为例)
抓取
def get_last_urls():
nodes_list = get_nodes_json()
process_nodes_list(nodes_list)
level1_url = get_level1_list(nodes_list)
last_urls = []
for url in url_list:
if url not in level1_url:
last_urls.append(url)
all_urls = []
for url in last_urls:
all_urls.append(parse.urljoin(WEB_CSDN, url))
all_urls.append(parse.urljoin(WEB_CSDN, url + '/recommend'))
all_urls.append(parse.urljoin(WEB_CSDN, url + '/closed'))
return all_urls
解析
def parse_list(url):
res_text = requests.get(url).text
sel = Selector(text=res_text)
all_trs = sel.xpath("//table[@class='forums_tab_table']//tbody//tr")
for tr in all_trs:
topic = Topic()
if tr.xpath(".//td[1]/span/text()").extract()[0]:
status = tr.xpath(".//td[1]/span/text()").extract()[0]
topic.status = status
存储
简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。 ORM 把数据库映射成对象。面向对象编程和关系型数据库,都是目前最流行的技术,但是它们的模型是不一样的。
面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。很早就有人提出,关系也可以用对象表达,这样的话,就能使用面向对象编程,来操作关系型数据库。
数据库 | Python |
---|---|
表(table) | 类(class) |
记录(record,行数据) | 对象(object) |
字段(field) | 对象的属性(attribute) |
from peewee import *
db = MySQLDatabase('spyder',host = '127.0.0.1', user='root',password = '123456')
class BaseModel(Model):
class Meta:
database = db
class Topic(BaseModel):
title = CharField()
content = TextField(default="")
id = IntegerField(primary_key=True)
四、并发爬虫
4.1 并行和并发
并发:
并发(Concurrent),在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
并发不是真正意义上的“同时进行”,只是CPU把一个时间段划分成几个时间片段(时间区间),然后在这几个时间区间之间来回切换,由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应用程序同时在进行。如:打游戏和听音乐两件事情在同一个时间段内都是在同一台电脑上完成了从开始到结束的动作。那么,就可以说听音乐和打游戏是并发的。
并行:
并行(Parallel),当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。
4.2 GIL
什么是GIL?
即全局解释器锁(global interpreter lock),每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说多线程并不是真正意义上的同时执行。
GIL是防止解释器多线程并发执行机器码的一个全局互斥锁。其存在主要是因为在机器码执行过程中,Cpython(使用C语言编写的Python解释器)的内存管理不是线程安全的。
所以,当每个线程在执行时都需要先获取GIL,保证同一时刻只有一个线程可以执行机器码。因此,当使用Cpython解释器解释多线程程序时,为了保证线程安全,即共享资源的原子性。默认使用了一个GIL,导致多线程实际是“伪多线程”。
程序分为计算密集型程序和I/O密集型程序。
计算密集型程序的特点是没有延时和阻塞,整段机器码几乎都用于计算的功能。
I/O密集型程序的特点是存在延时或堵塞,程序的功能主要是实现收发数据。
Cpython解释计算密集型程序时,多线程和单线程在运行时效上没有明显的区别,不过在解释I/O密集型程序时,多线程还是比单线程更快。因为在多线程中,当程序遇到堵塞或延时时,当前线程会释放GIL,让其他线程获取,因此多线程爬虫程序是比单线程程序在性能上有提升。
4.3 线程执行与同步
4.3.1 线程执行
join与setDaemon
- 子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束;
- 如果join()线程,那么主线程会等待子线程执行完再执行。
4.3.2 线程同步
- Lock
from threading import Lock
lock = Lock()
lock.acquire()
lock.release()
Lock有acquire()和release()方法,这两个方法必须是成对出现的,acquire()后面必须release()后才能再acquire(),否则会造成死锁
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
- RLock
- Condition
- Semaphore
- Queue
吐血,一不小心把chrome关掉了,从来…
五、动态页面处理(以京东商品为例)
5.1 区别分析
什么是动态网页?
动态网站就是调用数据库而构造的网站。在用户访问网页时,会调用其数据库。一般的动态网页是是以asp,jsp,php,aspx等结束。而且动态网站的开发成本比较高,耗时比较长。所以动态网站建设一般比较适合做企业网站。
动态网页优势
1、互动性好;
2、管理简单,通过数据库管理;
3、用户注册、登录;
4、时时更新数据;
5、前后端分离
6、反爬机制
与静态页面抓取数据不同,动态页面中的数据不能直接通过request+Selector的方式抓取。
5.2 京东商品信息爬取
以华为手机为例,其中商品名称、商品介绍信息等静态信息可以直接抓取,但是手机的价格、评论信息等内容都是通过动态加载的方式放入到html,因此比起静态网页需要多花点心思处理。
- url请求解析
优点 | 缺点 |
---|---|
性能高、结构清晰 | 复杂、多变、难抓 |
在商品调试页面 Network 中分析寻找商品的动态加载项,逆向找到请求的url 以该商品为例,反解出该商品价格请求的url https://p.3.cn/prices/mgets?skuIds=J_68605269870 (多找几个商品,确认字典key代表的含义),之后跟抓取静态页面相同。
(评论信息抓取同理)
- selenium抓取
优点 | 缺点 |
---|---|
简单、易操作 | 性能差 |
selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行处理(Selenium Grid)。Selenium的核心Selenium Core基于JsUnit,完全由JavaScript编写,因此可以用于任何支持JavaScript的浏览器上。
selenium爬虫中主要用来解决JavaScript渲染问题。
from selenium import webdriver
from scrapy import Selector
browser = webdriver.Chrome(executable_path='chromedriver.exe')
browser.get("https://item.jd.com/{}.html".format(good_id))
sel = Selector(text=browser.page_source)
# 之后跟静态页面相同
# 模拟点击过程
#按照节点标签点击节点
click_GoodInfo = browser.find_element_by_xpath("//li[@clstag='shangpin|keycount|product|shangpinpingjia_1']")
click_GoodInfo.click()
# 点击text中存在指定文本的节点
click_packaging = browser.find_element_by_xpath("//div[@class='tab-main large']//li[contains(text(),'规格与包装']")
click_packaging.click()
# 之后跟静态页面相同
- 优化selenium爬取速度
chrome_options = Options()
# 不启动chrome界面
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
# 不加载chrome图片
chrome_options.add_argumet("blink-settings=imagesEnabled=false")
browser = webdriver.Chrome(executable_path='chromedriver.exe',chrome_options=chrome_options)
六、模拟登陆验证
6.1 cookie和session
- Session(服务器维护)
客户端:输入账号密码提交到服务器
服务器:验证账号密码,验证通过,服务器把客户端信息以某种形式记录在服务器上(session),并以set-cookie的形式将sessionid返回给客户端 - Cookie(浏览器维护)
写在http协议的headers中,http请求服务器会带上cookie(sessionid)
6.2 滑动验证码识别(网站防反爬,貌似渐渐被淘汰)
- 鼠标移动到正确的元素上,显示没有缺口的图片,下载图片
- 点击元素显示有缺口的图片,下载图片
- 对比两张图片缺口的像素,计算拖动距离,模拟拖动行为
- 拖动滑块,进行匹配
七、反爬
7.1 常见的反爬策略
- User-agent反爬
from fake_useragent import UserAgent
ua = UserAgent()
headers = {"User-Agent": ua.random}
res =requests.get("url",headers = headers)
- ip访问频率
代理ip绕过反爬 - 账号访问频率
- 动态网页
- js逻辑加密和混淆
- 机器学习分析爬虫行为