简介
从0开始学习 Python 爬虫
项目源码:gitee.com/Actoress/sp…
爬虫数据采集分类
按照采集对象分类
- 全网采集
- 全站采集
- 具体网站的指定数据采集
按照采集方案分类
- 利用http协议采集 - 页面分析
- 利用api接口采集 - app数据采集
- 利用目标网站的api采集 - 微博 github twitter facebook
常用正则表达式
. :匹配任意字符(不包括换行符)
^ :匹配开始位置,多行模式下匹配每一行的开始
$ :匹配结束位置,多行模式下匹配每一行的结束
* :匹配前一个元字符0到多次
+ :匹配前一个元字符1到多次
? :匹配前一个元字符0到1次
{m, n} :匹配前一个元字符m到n次
\\ :转义字符,跟在其后的字符将失去作为特殊元字符的含义,例如\\.只能匹配,
[] :字符集,一个字符的集合,可匹配其中任意一个字符
| :逻辑表达式或, 比如a|b代表可匹配 a 或者 b
\b :匹配位于单词开始或结束为止的空字符串
\B :匹配不位于单词开始或结束为止的空字符串
\d :匹配一个数字,相当于[0-9]
\D :匹配非数字,相当于[^0-9]
\s :匹配任意空白字符,相当于[ \t\n\r\f\v]
\S :匹配非空白字符,相当于[^ \t\n\r\f\v]
\w :匹配数字、字母、下划线中任意一个字符,相当于[a-zA-Z0-9]
\W :匹配非数字、字母、下划线中任意字符,相当于[^a-zA-Z0-9]
使用beautifulsoup4
官方文档:beautifulsoup.readthedocs.io/zh_CN/v4.4.…
首先安装beautifulsoup4
pip3 install beautifulsoup4
引入beautifulsoup4
from bs4 import BeautifulSoup
使用find()
import re
from bs4 import BeautifulSoup
html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>淘趣指数</title>
</head>
<body>
<div id="div1" class="div1 div">
<span class="span1">
div1-span1
<i>div1-span1-i1</i>
</span>
<span>div2-span2</span>
</div>
<div id="div2" class="div2 div">测试用div文本2</div>
<div id="div3" class="div3 div">测试用div文本3</div>
<div id="div4" class="div4">测试用div文本4</div>
<div id="div5" class="div5">测试用div文本5</div>
<p>测试用文本2</p>
</body>
</body>
</html>
"""
bs = BeautifulSoup(html, "html.parser") # 参数1:需要解析的字符串,参数2所使用的解析库(文档中可查)
# 获取 title
title_tag = bs.title # 获取到 bs 中自定义的 tag 类
title_con = title_tag.string # 获取 tag 中的内容
# 获取div
div_tag = bs.find("div") # 返回第一条满足要求的元素
div_tags = bs.find_all("div") # 返回所有满足要求的元素,返回对象为一个数组
for item in div_tags:
print(item.string)
# 通过 id 获取
div_tag_id1 = bs.find(id="div5") # 获取 id=div5 的元素
div_tag_id2 = bs.find("div", id="div4") # 获取 id=div4 的 div
div_tag_re = bs.find_all("div", id=re.compile("div\d")) # 通过正则表达式获取
# 通过 class 获取
div_tag_class1 = bs.find("div", {"class": "div1"})
# 通过内容获取
div_tag_str = bs.find(string="测试用div文本1") # 返回的是 NavigableString 类型
# 获取子节点
parent = bs.find(id="div1")
child1 = parent.contents # 获取子节点
child2 = parent.descendants # 获取子节点的子节点
for item in child2:
# 因为纯在换行符所以需要判断name
if item.name:
print(item.name)
# 获取父节点
parent = bs.find("span", {"class": "span1"}).parent
# 获取所有祖先节点
parents = bs.find("span", {"class": "span1"}).parents
# 获取兄弟节点
next_tag = bs.find("span", {"class": "span1"}).next_sibling # 获取之后的一个兄弟节点
next_tags = bs.find("span", {"class": "span1"}).next_siblings # 获取之后的所有兄弟节点
previous_tag = bs.find("span", {"class": "span1"}).previous_sibling # 获取之前的一个兄弟节点
previous_tags = bs.find("span", {"class": "span1"}).previous_siblings # 获取之前的所有兄弟节点
# 获取节点的属性
div_tag = bs.find("div", {"class": "div1"})
print(div_tag["class"])
print(div_tag.get("class"))
使用 Requests 爬虫
官方文档:2.python-requests.org/zh_CN/lates…
使用 Scrapy 爬虫
安装 Scrapy
pip3 install lxml # 必备依赖
pip3 install Scrapy
引入 Selector
from scrapy import Selector
Selector 操作 xpath
import requests
from scrapy import Selector
html = requests.get("https://www.hao123.com/")
html_text = html.text
sel = Selector(text=html_text) # 初始化
tag = sel.xpath('//*[@id="sites2_wrapper"]/div[3]/ul/li[4]/div/a')
print(tag.extract()[0]) # 获取标签
text = sel.xpath('//*[@id="sites2_wrapper"]/div[3]/ul/li[4]/div/a/text()')
print(text.extract()[0]) # 获取内容
Selector 操作 css 选择器
import requests
from scrapy import Selector
html = requests.get("https://www.hao123.com/")
html_text = html.text
sel = Selector(text=html_text) # 初始化
wrapper_class = sel.css(".wrapper")
print(wrapper_class.extract()[0]) # 获取标签
wrapper_id = sel.css("#wrapper::text")
print(wrapper_id.extract()[0]) # 获取内容
GIL
什么是GIL
GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。 每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行)
某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是"通行证",并且在一个python进程中,GIL只有一个。拿不到通行证的线程 ,就不允许进入CPU执行
GIL会释放的:
- 1.时间片释放 - 指定时间释放
- 2.遇到IO释放
GIL的作用:
- GIL保证安全,但是GIL又有时间片的概念
- GIL会保证字节码的安全
【重要】线程间的同步
目的:希望我这个线程在执行的过程中其它的线程不要执行,等待我这个线程执行完成后,再继续执行后面的线程
应用场景:电商库存,投票票数
实例代码
"""
python 多线程爬虫学习
线程间的同步:
当我们运行代码时,不希望代码被中断
"""
from threading import Thread
from threading import Lock
total = 0
# 建立一个通用线程锁,注意使用同一把锁,如果有一个线程获取到了这把锁,另一个线程就必须等待这个锁释放
total_lock = Lock()
def add():
total_lock.acquire() # 加锁
global total
for i in range(1000):
total += 1
total_lock.release() # 释放锁
def desc():
total_lock.acquire() # 加锁
global total
for i in range(1000):
total -= 1
total_lock.release() # 释放锁
if __name__ == "__main__":
# 线程传递参数
t1 = Thread(target=add)
t1.start()
t2 = Thread(target=desc)
t2.start()
t1.join() # t1 运行完成后才会执行之后的逻辑
t2.join() # t2 运行完成后才会执行之后的逻辑
print(total) # => 0