爬虫原理
网络连接需要计算机一次Request请求和服务器端的Response回应。爬虫也需要做两件事:
- 模拟计算机对服务器发起Request请求
- 接收服务器端的Response内容并解析、提取所需要的信息。
Python第三方库的安装
在PyCharm中安装
- 打开PyCharm,在菜单栏中选择File|Default Settings 命令
- 选择左侧的 Project Interpreter选项,在窗口右侧选择Python环境
- 单击右侧窗口左下角的➕号添加第三方库
- 输入三方库名称,选中需要下载的库
- 单击 Install Package 按钮安装(注:Windows平台下需要勾选 Install to user sit 复选框)。
在PIP中安装
安装Python之后,PIP会同时进行安装,验证PIP是否安装成功
pip3 --version复制代码
安装Python三方库
pip3 install packagename复制代码
注:如果使用Python2,pip3改为 pip 使用
手动安装
-
在网站下载所需要安装的库
-
命令行中输入
pip3 install wheel复制代码
- cd到所下载库在本地的位置执行命令行
pip3 install packagename.whl复制代码
推荐方法为 PIP安装 和 本地手动安装 方式。
第一个简单的爬虫
利用 request
获取网络数据,利用BeautifulSoup
解析数据。获取的数据自如网站租房信息的房屋信息Title。
一段代码
import requestsfrom bs4 import BeautifulSoup #导入相应的库headers = { 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}#模拟浏览器请求头res = requests.get('http://www.ziroom.com/z/nl/-z3.html?qwd=%E4%BA%AE%E9%A9%AC%E6%A1%A5',headers = headers) #请求网页# print(res.text)soup = BeautifulSoup(res.text,'html.parser') # 解析数据# print(soup.prettify())# price = soup.select('#houseList > li:nth-child(2) > div.txt > h3')names = soup.select('#houseList > li:nth-of-type(2) > div.txt > h3')# print(name.getText())# names = soup.select('#houseList > li > div.txt > h3 > a')#for name in names: print(name.getText())复制代码
代码分析:
- 关于headers
加入请求头来伪装陈浏览器,以便更好的获取数据。User-Agent
获取方式,打开开发者工具,刷新网页后即可获取
- select方法
soup.select('div.item > a > h1') #括号中的内容可通过浏览器复制得到复制代码
(1)鼠标定位到需要提取数据的位置,右击选择“检查”命令
(2)在网页源码中右击所选元素,在弹出的菜单中选择“Copy selector”,可得到:
#houseList > li:nth-child(2) > div.txt > h3复制代码
注:
nth-child(2)
中的2表示的是所取得是列表中的第二个数据。
这个在Python中运行会报错,需要将其改为nth-of-type(2)
。
如果需要获取整个列表的所有信息,只需将nth-of-type(2)去掉就可以了。像这样:
names = soup.select('#houseList > li > div.txt > h3 > a')复制代码
- getText()方法
使用该方法获取文字信息。
使用前获取的数据是这样的
豪宅 · 星源汇1居室-西
复制代码
使用后获取的数据是这样的
豪宅 · 星源汇1居室-西复制代码
- get('attr')
用来获取属性信息,此代码中暂未用到。
最后再来个稍微复杂的例子来说明更多使用小技巧,不解释了。
实现效果,获取酷狗Top500的歌曲信息
import requestsfrom bs4 import BeautifulSoupimport timeheaders = { 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}def get_music(url): res = requests.get(url,headers=headers) # print(res.text) # soup = BeautifulSoup(res.text,'lxml') soup = BeautifulSoup(res.text, 'html.parser') # print(soup.prettify()) ranks = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > span.pc_temp_num') titles = soup.select('#rankWrap > div.pc_temp_songlist > ul > li > a') for rank , title in zip(ranks,titles): # 多个数据的处理方式 data = { 'rank' : rank.getText().strip(), # 字符串的处理方式,去除两侧的空格 'title' : title.getText(), } print(data)if __name__ == '__main__': urls = ['http://www.kugou.com/yy/rank/home/{}-8888.html?from=rank'.format(str(i)) for i in range(1,24)] i = 0 for url in urls: i = i + 1 print('第{}页'.format(i)) get_music(url) time.sleep(2) #以防数据获取速度太快出现问题复制代码
使用正则表达式来解析数据
正则表达式常用字符
一般字符
- "." 匹配任意单个字符。例如 a.b ,可以匹配为abc、aic、a&c等,但不包含换行符
- "" 转义字符,把字符改变为原来的意思。
- [...] 字符集,相当于括号中任选一个。例如a[bcd],可以匹配为 ab、ac、ad。
预定义字符
数量词
- "*" 匹配前一个字符0或无限次
- "+" 至少匹配前一个字符一次
- "?" 匹配前一个字符0次或者1次
- "{m}" 匹配前一个字符 m 次
- "{m,n}"匹配前一个字符m至n词
边界匹配
re模块
re模块拥有Python语言拥有的所有的正则表达式功能。
search()函数
用于匹配并提取第一个符合规律的内容。
语法如下:
re.search(pattern,string,flags=0)复制代码
- pattern 为匹配的正则表达式
- string 要匹配的字符串
- flags 标志位,用于控制正则表达式的匹配方式,如是否区分大小写,多行匹配等。
例子:
import rea = 'one12twp2three33four'infos = re.search('\d+',a)print(infos) # <_sre.SRE_Match object; span=(3, 5), match='12'>print(infos.group()) # group方法获取信息 12复制代码
sub()函数
用于替换字符串中的匹配项。
语法如下:
re.sub(pattern,repl,string,count=0,flags=0)复制代码
- pattern 匹配的正则表达式
- repl 替换的字符串
- string 要被查找替换的原始字符串
- count 模式匹配后替换的最大次数,默认0表示替换所有的匹配
- flags 标志位,控制正则表达式的匹配方式
例子:
import rephone = '181-1324-1341'newphone = re.sub('\D+','',phone)print(newphone) #18113241341复制代码
findall()函数
匹配所有符合规律的内容,并以列表的形式返回结果。
例子:
import rea = 'one12twp2three33four'infos = re.findall('\d+',a)print(infos) #['12', '2', '33']infos = re.findall('\d',a)print(infos) #['1', '2', '2', '3', '3']复制代码
完整的示例
获取斗破苍穹小说全部内容并存到本地文件中
import requestsimport reimport timef = open('/Users/fangjiayou/Desktop/Last/untitled001.txt','a+')def get_info(url): req = requests.get(url) if req.status_code == 200: contents = re.findall('(.*?)
',req.content.decode('utf-8'),re.S) for content in contents: f.write(content+'\n\n') else: passif __name__ == '__main__': urls = ['http://www.doupoxs.com/doupocangqiong/{}.html'.format(str(number)) for number in range(2,1625)] for url in urls: print(url) get_info(url) f.close() #关闭文件,防止内存问题复制代码
Lxml库的使用并存放数据到excel中
三方库 xlwt的使用
写入Excel中需要的三方库
pip3 install xlwt复制代码
使用方式
import xlwtbook = xlwt.Workbook(encoding='utf-8') # 创建工作簿sheet = book.add_sheet('Sheet1') # 创建工作表sheet.write(0,0,'python') # 在相应的单元格写入数据sheet.write(1,1,'love')book.save('test.xls') # 保存到文件中复制代码
实现效果:
举例 获取起点中文网小说信息
包含了lxml
解析方式的使用
先上代码
import xlwtimport requestsimport timefrom lxml import etreeall_info_list = [] # 初始化列表,存入爬虫数据def get_info(url): # 定义获取爬虫信息的函数 req = requests.get(url) html = etree.HTML(req.text) infos = html.xpath('//ul[@class="all-img-list cf"]/li')# 定位大标签,一次循环 for info in infos: title = info.xpath('div[2]/h4/a/text()')[0] author = info.xpath('div[2]/p[1]/a[1]/text()')[0] complete = info.xpath('div[2]/p[1]/span/text()')[0] introduce = info.xpath('div[2]/p[2]/text()')[0].strip() info_list = [title,author,complete,introduce] all_info_list.append(info_list) print(info_list) time.sleep(1) # 睡眠1sif __name__ == '__main__': urls = ['https://www.qidian.com/all?orderId=&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page={}'.format(str(number)) for number in range(0,2)] for url in urls: get_info(url) header = ['书名','作者','结束','简介'] book = xlwt.Workbook(encoding='utf-8') # 创建工作簿 sheet = book.add_sheet('Sheet1') #创建工作表 for h in range(len(header)): sheet.write(0,h,header[h]) # 写入表头 row = 1 for list in all_info_list: colum = 0 for data in list: sheet.write(row,colum,data) colum = colum + 1 row = row + 1 book.save('起点小说.xls') #保存文件复制代码
解析JSON数据
Python中有解析JSON数据的标准库,使用方式:
import json复制代码
使用方法很简单,只需一段代码说明:
import jsonjsonstring = '{"user_man":[{"name":"peter"},{"name":"xiaoming"}],'\ '"user_woman":[{"name":"Anni"},{"name":"zhangsan"}]}'jsondata = json.loads(jsonstring)print(jsondata.get("user_man")) #[{'name': 'peter'}, {'name': 'xiaoming'}]print(jsondata.get('user_woman')[0].get('name')) #Anni复制代码
如何爬取图片
使用URLretrieve模块
URLretrieve
模块是URLlib.request中的一部分,用法:
urlretrieve(url,path)复制代码
使用代码示例:
path = '/Users/fang/Desktop/Last/'urlstr = 'http://g.hiphotos.baidu.com/image/h%3D300/sign=fb8af6169d2397ddc9799e046983b216/0823dd54564e92584fbb491f9082d158cdbf4eb0.jpg'urlretrieve(urlstr,path+urlstr[-10:])复制代码
通过写入文件来获取图片
path = '/Users/fangjiayou/Desktop/Last/'urlstr = 'http://g.hiphotos.baidu.com/image/h%3D300/sign=fb8af6169d2397ddc9799e046983b216/0823dd54564e92584fbb491f9082d158cdbf4eb0.jpg'data = requests.get(urlstr)fp = open(path+urlstr[-10:],'wb')fp.write(data.content)fp.close()复制代码
解析JSON数据并获取图片
示例流程为:根据关键词搜索对应的图片,解析数据进行图片下载。
代码:
from bs4 import BeautifulSoupimport requestsimport jsonheader = { 'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}searchPicURL = 'https://www.pexels.com/search/'word = input('请输入要下载的图片:')url = searchPicURL+wordprint(url)data = requests.get(url)soup = BeautifulSoup(data.text,'lxml')images = soup.select('body > div.page-wrap > div.l-container > div.photos > article > a.js-photo-link > img')path = '/Users/fangjiayou/Desktop/Last/'i = 1for image in images: print('开始写入第{}张'.format(i)) src = image.get('src') data = requests.get(src) relativepath = path+src.split('?')[0][-10:] fp = open(relativepath,'wb') fp.write(data.content) fp.close() print('第{}张写入结束'.format(i)) i = i + 1复制代码
多线程的使用
Python中多线程爬虫使用multiprocess
库,
使用方法:
from multiprocess import Pooldef method(url): print(url)pool = Pool(processes=4)pool.map(method,['1','2','3'])# method 为需要运行的函数,后面为迭代参数复制代码
通过爬取糗事百科数据来说明使用多线程的效率提升,代码如下
import requestsimport refrom multiprocess import Poolimport timeheaders = { 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}def re_scraper(url): req = requests.get(url,headers=headers) titles = re.findall('(.*?)
',req.text,re.S) contents = re.findall('\n (.*?)',req.text,re.S) # for title,content in zip(titles,contents): # print('Title:'+title+'\nContent:'+content)if __name__ == '__main__': urls = ['https://www.qiushibaike.com/text/page/{}/'.format(str(page)) for page in range(1,30)] print("开始串行爬虫") start1 = time.time() for url in urls: re_scraper(url) end1 = time.time() print('串行爬虫消耗时间:',end1-start1) print('两个线程开始') start2 = time.time() pool2 = Pool(processes=2) pool2.map(re_scraper,urls) end2 = time.time() print('两个线程消耗时间:',end2-start2) print('四个线程开始') start4 = time.time() pool4 = Pool(processes=4) pool4.map(re_scraper, urls) end4 = time.time() print('四个线程消耗时间:', end4 - start4)复制代码
结果如下:
开始串行爬虫串行爬虫消耗时间: 5.8548901081085205两个线程开始两个线程消耗时间: 2.986906051635742四个线程开始四个线程消耗时间: 1.016632080078125复制代码
注:并不是线程越多效率越高,根据硬件设置线程数才是最好的选择
----------------------------持续更新---------------------------