爬虫
抓取页面
requests / aiohttp / httpx (抓取页面较为简单,所以就不细讲,只列出方法,不会可自行搜索学习,简单易上手)
解析页面
正则表达式 —>re
首先需要导入re和requests模块
import re
import requests
使用requests.get(‘网址’) ——向网站发送get请求
resp = requests.get(
url='https://www.sohu.com',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
}
)
注意(如果无法请求成功可添加User-Agent和cookie以增大请求数据成功的概率)
cookie可在网页中鼠标右键检查选择network,然后刷新网页之后点击刷新项查看headers,就可以获取cookie值
请求成功之后就可使用正则表达式进行匹配标签
# 使用正则表达式捕获组从页面代码中提取电影名称
patten = re.compile(r'\<span class="title"\>(.*?)\<\/span\>')
print(patten.findall(resp.text))
注意:\为取消符号特殊意义,(.*?)为抓取目标
抓取成功之后保存到文件夹或者数据库即可
css选择器 —>beautifulsoup4
想要使用css选择器首先得安装第三方库
在pycharm的terminal中输入pip install beautifulsoup4回车即可,显示安装成功后就可使用该第三方库
导入bs4模块
import bs4
向网站发送请求一般选取requests,参考上面即可
请求成功之后,去找到网页的代码(网站中鼠标右键检查),根据代码的标签寻找自己想要爬取内容的标签
soup = bs4.BeautifulSoup(resp.text, 'html.parser')
spans = soup.select('ul.li>div5>a')
这里写的(‘ul.li>div5>a’)意思是ul标签下的li子标签,li标签下的第五个div下的a标签
抓取成功保存即可
xpath —>从XML文件中获取元素的查询语法 —> lxml
使用xpath想要安装第三方库lxml
terminal终端输入pip install lxml,显示安装成功后就可使用该第三方库
导入lxml
from lxml import etree
使用requests向网站发送请求,成功后
去网站找到html代码,选中自己要获取的内容,鼠标右键,找到copy,找到copy xpath点击
root = etree.HTML(resp.text)
spans = root.xpath('//*[@id="content"]/div/div[1]/ol/li/div/div[2]/div[1]/a/span[1]')
注意:粘贴的xpth格式只有当前选中的一行数据,可根据自己需要的数据对代码进行修改,即可拿到想要的代码
例如去掉div[2]中的[2]
拿到数据下载保存即可
保存数据(数据持久化)
上面讲了如何爬取数据,这里将会告诉大家如何保存数据
Execl
该库是保存到表格中
安装第三方库 pip install xlwt openpyxl
导入模块xlwt
import xlwt
下面给到家参考一份我写的完整的保存数据到Excel中的代码
def main():
# 创建工作簿
wb = xlwt.Workbook()
# 创建工作表
sheet = wb.add_sheet('Top250')
col_names = ('排名', '名称', '评分', '类型', '制片国家', '语言', '时长')
for index, name in enumerate(col_names):
sheet.write(0, index, name)
rank = 0
for page in range(10):
resp = requests.get(
url=f'https://movie.douban.com/top250?start={page * 25}',
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
}
)
soup = bs4.BeautifulSoup(resp.text, 'html.parser')
info_divs = soup.select('div.info')
for info_div in info_divs:
rank += 1
anchor = info_div.select_one('div.hd>a')
detail_url = anchor.attrs['href']
title = anchor.select_one('span.title').text
score = info_div.select_one('div.bd>div.star>span.rating_num').text
movie_details = [rank, title, score]
movie_details += fetch_movie_detail(detail_url)
for index, item in enumerate(movie_details):
sheet.write(rank, index, item)
wb.save('豆瓣电影.xls')
print('程序结束')
if __name__ == '__main__':
main()
CSV —>reader / writer(file)
CSV操作我给大家参考一份下载图片的代码,由于好理解,我就不详细给大家解释了,大家根据代码参考就可
resp = requests.get('https://bkimg.cdn.bcebos.com/pic/562c11dfa9ec8a1363271293564a868fa0ec08fa6a24?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2UxMTY=,g_7,xp_5,yp_5')
with open('sanji.png', 'wb') as file:
file.write(resp.content)
也可将数据保存到数据库当中,这种方法一般企业使用更多,队友新手党来说,学会上面两种方法即可
数据库(Database)
关系型数据库(SQL)
库(NoSQL —> NewSQL)
并发爬取
由于爬取方式是一个一个下载,可能速度会相对较慢,我们就可以使用多线程或者多进程爬取,也可使用线程池或者进程池
多线程
多线程是由一个进程中的多个线程组成,多个线程可以同时进行文件的下载,适用于下载文件之间没有先后顺序的下载
多线程之间通信比较简单,因为线程可以共享进程的内容
使用多线程需要导入模块Thread
from threading import Thread
这里我用代码向大家演示多线程的使用方法
def record_time(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__}执行时间:{end - start:.3f}秒')
return result
return wrapper
@record_time
def download(filename):
print(f'开始下载{filename}')
time.sleep(random.randint(5, 10))
print(f'{filename}下载完成')
@record_time
def upload(filename):
print(f'开始上传{filename}')
time.sleep(random.randint(5, 10))
print(f'{filename}上传完成')
# 如果程序里有好时间的任务,可能会阻塞别的任务,就用线程来执行,如果有因果关系就必须等着,按顺序执行,如果没有因果关系,就可以用线程
@record_time
def main():
t1 = Thread(target=download, args=('Python从入门到住院.pdf', ))
t1.start()
t2 = Thread(target=download, args=('MySQL从删库到跑路.avi', ))
t2.start()
t3 = Thread(target=upload, args=('北京有点热.avi', ))
t3.start()
t1.join()
t2.join()
t3.join()
if __name__ == '__main__':
main()
注意:args后面为元组,所以需要在最后打逗号
这里我使用了一个装饰器,用来装饰下面三个函数
线程池
使用线程池需要导入模块ThreadPoolExecutor
from concurrent.futures.thread import ThreadPoolExecutor
使用方法我之间用代码向大家演示
ef download_picture(picture_url):
resp = requests.get(picture_url)
if resp.status_code == 200:
filename = picture_url[picture_url.rfind('/') + 1:]
with open(f'images/{filename}', 'wb') as file:
file.write(resp.content)
with ThreadPoolExecutor(max_workers=16) as pool:
for num in range(1, 11):
resp = requests.get('https://image.so.com/zjl?ch=beauty&t1=600&sn=30')
data_dict = resp.json()
for beauty_dict in data_dict['list']:
picture_url = beauty_dict['qhimg_url']
pool.submit(download_picture, picture_url)
多线程和多进程
使用方法和多线程,线程池差不多
将Tread替换为Process,ThreadPoolExecutor替换为ProcessPoolExecutor即可
异步编程
异步不需要排队,没有顺序,不会阻塞,也不会等待
需要模块aiohttp和asyncio
import aiohttp
import asyncio
直接代码展示
U_A = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36'
urls = [
'https://www.bilibili.com/',
'https://www.baidu.com/',
'https://www.sohu.com/',
'https://www.taobao.com/',
'https://www.jd.com/',
'https://www.qidian.com/',
'https://www.huya.com/',
'https://www.douyu.com/'
]
title_pattern = re.compile(r'\<title\>(?P<foo>.*?)\<\/title\>')
async def main(url):
async with aiohttp.ClientSession() as session:
async with session.get(url, headers={'User-Agent': U_A}) as resp:
page_code = await resp.text()
match = title_pattern.search(page_code)
if match:
print(match.group('foo'))
cos_list = [main(url) for url in urls]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(cos_list))
loop.close()