记python爬虫初接触-lv0
记python爬虫初接触-lv0
目前主流而合法的网络数据收集方法,主要分为3类:
- 开放数据集下载;
- API读取;
- 爬虫。
很久之前就想学习一下python爬虫了,但是一直没有执行,刚入门慢慢来叭
有转载内容来自:
https://blog.csdn.net/depers15/article/details/52214523
https://blog.csdn.net/nkwshuyi/article/details/79435248?spm=1001.2014.3001.5502
https://www.jb51.net/article/144104.htm
基础爬虫的固定模式
这里讲的是不需要处理像异步加载、验证码、代理等高阶爬虫技术的爬虫方法。一般而言,基础爬虫的两大请求库 urllib 和 requests 中 requests 通常为大多数人所钟爱,当然 urllib 也功能齐全。两大解析库 BeautifulSoup 因其强大的 HTML 文档解析功能而备受青睐,另一款解析库 lxml 在搭配 xpath 表达式的基础上也效率提高。就基础爬虫来说,两大请求库和两大解析库的组合方式可以依个人偏好来选择。
比较常用爬虫组合工具:
requests + BeautifulSoup
requests + lxml
同一网页爬虫的四种实现方式:
以腾讯新闻首页的新闻信息抓取为例:
比如说我们想抓取每个新闻的标题和链接,并将其组合为一个字典的结构打印出来。首先查看 HTML 源码确定新闻标题信息组织形式。
目标信息存在于 h3 标签元素下 a 标签内的文本和 href 属性中。可直接利用 requests 库构造请求,并用 BeautifulSoup 或者 lxml 进行解析。
方式一: requests + BeautifulSoup + select css选择器
# select method
import requests
from bs4 import BeautifulSoup
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36'}
url = 'http://news.qq.com/'
Soup = BeautifulSoup(requests.get(url=url, headers=headers).text.encode("utf-8"), 'lxml')
em = Soup.select('em[class="f14 l24"] a')
for i in em:
title = i.get_text()
link = i['href']
print({'标题': title,
'链接': link
})
方式二: requests + BeautifulSoup + find_all 进行信息提取
# find_all method
import requests
from bs4 import BeautifulSoup
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36'}
url = 'http://news.qq.com/'
Soup = BeautifulSoup(requests.get(url=url, headers=headers).text.encode("utf-8"), 'lxml')
em = Soup.find_all('em', attrs={'class': 'f14 l24'})for i in em:
title = i.a.get_text()
link = i.a['href']
print({'标题': title,
'链接': link
})
方式三: requests + lxml/etree + xpath 表达式
# lxml/etree method
import requests
from lxml import etree
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36'}
url = 'http://news.qq.com/'
html = requests.get(url = url, headers = headers)
con = etree.HTML(html.text)
title = con.xpath('//em[@class="f14 l24"]/a/text()')
link = con.xpath('//em[@class="f14 l24"]/a/@href')
for i in zip(title, link):
print({'标题': i[0],
'链接': i[1]
})
使用 lxml 库下的 etree 模块进行解析,然后使用 xpath 表达式进行信息提取,效率要略高于 BeautifulSoup + select 方法。这里对两个列表的组合采用了 zip 方法
方式四: requests + lxml/html/fromstring + xpath 表达式
# lxml/html/fromstring method
import requests
import lxml.html as HTML
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36'}
url = 'http://news.qq.com/'
con = HTML.fromstring(requests.get(url = url, headers = headers).text)
title = con.xpath('//em[@class="f14 l24"]/a/text()')
link = con.xpath('//em[@class="f14 l24"]/a/@href')
for i in zip(title, link):
print({'标题': i[0],'链接': i[1]
})
跟方法三类似,只是在解析上使用了 lxml 库下的 html.fromstring 模块
爬虫知识点多,需要前端、python、数据库、正则表达式、XPath表达式等知识。对于一个简单网页的数据抓取,多尝试几种抓取方案,举一反三,也许会对python爬虫有更深的理解。
一些步骤:
1、基本抓取网页
get方法和post方法
2、使用代理IP
在开发爬虫过程中经常会遇到IP被封掉的情况,这时就需要用到代理IP;
在urllib2包中有ProxyHandler类,通过此类可以设置代理访问网页
3、Cookies处理
cookies是某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密),python提供了cookielib模块用于处理cookies,cookielib模块的主要作用是提供可存储cookie的对象,以便于与urllib2模块配合使用来访问Internet资源
关键在于CookieJar(),它用于管理HTTP cookie值、存储HTTP请求生成的cookie、向传出的HTTP请求添加cookie的对象。整个cookie都存储在内存中,对CookieJar实例进行垃圾回收后cookie也将丢失,所有过程都不需要单独去操作。
可手动添加cookie
4、伪装成浏览器
某些网站反感爬虫的到访,于是对爬虫一律拒绝请求。所以用urllib2直接访问网站经常会出现HTTP Error 403: Forbidden的情况
对有些 header 要特别留意,Server 端会针对这些 header 做检查
User-Agent 有些 Server 或 Proxy 会检查该值,用来判断是否是浏览器发起的 Request
Content-Type 在使用 REST 接口时,Server 会检查该值,用来确定 HTTP Body 中的内容该怎样解析。
这时可以通过修改http包中的header来实现
5、验证码的处理
对于一些简单的验证码,可以进行简单的识别
6、gzip压缩
遇到过某些网页,不论怎么转码都是一团乱码。许多web服务具有发送压缩数据的能力,这可以将网络线路上传输的大量数据消减 60% 以上。这尤其适用于 XML web 服务,因为 XML 数据 的压缩率可以很高。
但是一般服务器不会为你发送压缩数据,除非你告诉服务器你可以处理压缩数据。
于是需要修改代码
这是关键:创建Request对象,添加一个 Accept-encoding 头信息告诉服务器你能接受 gzip 压缩数据
7、多线程并发抓取
单线程太慢的话,就需要多线程了,这里给个简单的线程池模板 这个程序只是简单地打印了1-10,但是可以看出是并发的。python的多线程对于爬虫这种网络频繁型,还是能一定程度提高效率的。
另一个超详细教程:
请参考:
https://blog.csdn.net/nkwshuyi/article/details/79435248?spm=1001.2014.3001.5502
安装Anaconda套装……(省略一万步
代码部分:
读入网页加以解析抓取,需要用到的软件包是 requests_html 。我们此处并不需要这个软件包的全部功能,只读入其中的 HTMLSession 就可以。
from requests_html import HTMLSession
然后,我们建立一个会话(session),即让Python作为一个客户端,和远端服务器交谈。
session = HTMLSession()
示例:我们打算采集信息的网页,是《如何用《玉树芝兰》入门数据科学?》一文。
我们找到它的网址,存储到url变量名中。
url = 'https://www.jianshu.com/p/85f4624485b9'
下面的语句,利用 session 的 get 功能,把这个链接对应的网页整个儿取回来。
r = session.get(url)
网页里面都有什么内容呢?
我们告诉Python,请把服务器传回来的内容当作HTML文件类型处理。我不想要看HTML里面那些乱七八糟的格式描述符,只看文字部分。
于是我们执行:
print(r.html.text)
这就是获得的结果了:
我们心里有数了。取回来的网页信息是正确的,内容是完整的。
好了,我们来看看怎么趋近自己的目标吧。
我们先用简单粗暴的方法,尝试获得网页中包含的全部链接。
把返回的内容作为HTML文件类型,我们查看 links 属性:
r.html.links
这是返回的结果:
这里许多链接,看似都不完全。例如第一条结果,只有:
'/'
这是什么东西?是不是链接抓取错误啊?
不是,这种看着不像链接的东西,叫做相对链接。它是某个链接,相对于我们采集的网页所在域名(https://www.jianshu.com)的路径。
这就好像我们在国内邮寄快递包裹,填单子的时候一般会写“XX省XX市……”,前面不需要加上国家名称。只有国际快递,才需要写上国名。
但是如果我们希望获得全部可以直接访问的链接,怎么办呢?
很容易,也只需要一条 Python 语句。
r.html.absolute_links
这里,我们要的是“绝对”链接,于是我们就会获得下面的结果:
这回看着是不是就舒服多了?
我们的任务已经完成了吧?链接不是都在这里吗?
链接确实都在这里了,可是跟我们的目标是不是有区别呢?
检查一下,确实有。
我们不光要找到链接,还得找到链接对应的描述文字呢,结果里包含吗?
没有。
结果列表中的链接,都是我们需要的吗?
不是。看长度,我们就能感觉出许多链接并不是文中描述其他数据科学文章的网址。
这种简单粗暴直接罗列HTML文件中所有链接的方法,对本任务行不通。
那么我们该怎么办?
我们得学会跟 Python 说清楚我们要找的东西。这是网页抓取的关键。
想想看,如果你想让助手(人类)帮你做这事儿,怎么办?
你会告诉他:
“寻找正文中全部可以点击的蓝色文字链接,拷贝文字到Excel表格,然后右键复制对应的链接,也拷贝到Excel表格。每个链接在Excel占一行,文字和链接各占一个单元格。”
虽然这个操作执行起来麻烦,但是助手听懂后,就能帮你执行。
同样的描述,你试试说给电脑听……不好意思,它不理解。
因为你和助手看到的网页,是这个样子的。
电脑看到的网页,是这个样子的。
为了让你看得清楚源代码,浏览器还特意对不同类型的数据用了颜色区分,对行做了编号。
数据显示给电脑时,上述辅助可视功能是没有的。它只能看见一串串字符。
那可怎么办?
仔细观察,你会发现这些HTML源代码里面,文字、图片链接内容前后,都会有一些被尖括号括起来的部分,这就叫做“标记”。
所谓HTML,就是一种标记语言(超文本标记语言,HyperText Markup Language)。
标记的作用是什么?它可以把整个的文件分解出层次来。
如同你要发送包裹给某个人,可以按照“省-市-区-街道-小区-门牌”这样的结构来写地址,快递员也可以根据这个地址找到收件人。
同样,我们对网页中某些特定内容感兴趣,可以依据这些标记的结构,顺藤摸瓜找出来。
这是不是意味着,你必须先学会HTML和CSS,才能进行网页内容抓取呢?
不是的,我们可以借助工具,帮你显著简化任务复杂度。
这个工具,Google Chrome浏览器自带。
我们在样例文章页面上,点击鼠标右键,在出现的菜单里面选择“检查”。
这时,屏幕下方就会出现一个分栏。
我们点击这个分栏左上角(上图红色标出)的按钮。然后把鼠标悬停在第一个文内链接(《玉树芝兰》)上面,点击一下。
此时,你会发现下方分栏里面,内容也发生了变化。这个链接对应的源代码被放在分栏区域正中,高亮显示。
确认该区域就是我们要找的链接和文字描述后,我们鼠标右键选择高亮区域,并且在弹出的菜单中,选择 Copy -> Copy selector。
找一个文本编辑器,执行粘贴,就可以看见我们究竟复制下来了什么内容。
body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a
这一长串的标记,为电脑指出了:请你先找到 body 标记,进入它管辖的这个区域后去找 div.note 标记,然后找……最后找到 a 标记,这里就是要找的内容了。
回到咱们的 Jupyter Notebook 中,用刚才获得的标记路径,定义变量sel。
sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a'
我们让 Python 从返回内容中,查找 sel 对应的位置,把结果存到 results 变量中。
results = r.html.find(sel)
我们看看 results 里面都有什么。
results
这是结果:
[<Element 'a' href='https://www.jianshu.com/nb/130182' target='_blank'>]
results 是个列表,只包含一项。这一项包含一个网址,就是我们要找的第一个链接(《玉树芝兰》)对应的网址。
可是文字描述“《玉树芝兰》”哪里去了?
别着急,我们让 Python 显示 results 结果数据对应的文本。
results[0].text
这是输出结果:
'玉树芝兰'
我们把链接也提取出来:
results[0].absolute_links
显示的结果却是一个集合。
{'https://www.jianshu.com/nb/130182'}
我们不想要集合,只想要其中的链接字符串。所以我们先把它转换成列表,然后从中提取第一项,即网址链接。
list(results[0].absolute_links)[0]
这次,终于获得我们想要的结果了:
'https://www.jianshu.com/nb/130182'
有了处理这第一个链接的经验,你信心大增,是吧?
其他链接,也无非是找到标记路径,然后照猫画虎嘛。
可是,如果每找一个链接,都需要手动输入上面这若干条语句,那也太麻烦了。
这里就是编程的技巧了。重复逐条运行的语句,如果工作顺利,我们就要尝试把它们归并起来,做个简单的函数。
对这个函数,只需给定一个选择路径(sel),它就把找到的所有描述文本和链接路径都返回给我们。
def get_text_link_from_sel(sel): mylist = [] try: results = r.html.find(sel) for result in results: mytext = result.text mylink = list(result.absolute_links)[0] mylist.append((mytext, mylink)) return mylist except: return None
我们测试一下这个函数。
还是用刚才的标记路径(sel)不变,试试看。
print(get_text_link_from_sel(sel))
输出结果如下:
[('玉树芝兰', 'https://www.jianshu.com/nb/130182')]
没问题,对吧?
好,我们试试看第二个链接。
我们还是用刚才的方法,使用下面分栏左上角的按钮点击第二个链接。
下方出现的高亮内容就发生了变化:
我们还是用鼠标右键点击高亮部分,拷贝出 selector。
然后我们直接把获得的标记路径写到 Jupyter Notebook 里面。
sel = 'body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a'
用我们刚才编制的函数,看看输出结果是什么?
print(get_text_link_from_sel(sel))
输出如下:
[('如何用Python做词云?', 'https://www.jianshu.com/p/e4b24a734ccc')]
检验完毕,函数没有问题。
下一步做什么?
你还打算去找第三个链接,仿照刚才的方法做?
那你还不如全文手动摘取信息算了,更省事儿一些。
我们要想办法把这个过程自动化。
对比一下刚刚两次我们找到的标记路径:
body > div.note > div.post > div.article > div.show-content > div > p:nth-child(4) > a
以及:
body > div.note > div.post > div.article > div.show-content > div > p:nth-child(6) > a
发现什么规律没有?
对,路径上其他的标记全都是一样的,唯独倒数第二个标记("p")后冒号后内容有区别。
这就是我们自动化的关键了。
上述两个标记路径里面,因为指定了在第几个“子”(nth-child)文本段(paragraph,也就是"p"代表的含义)去找"a"这个标记,因此只返回来单一结果。
如果我们不限定"p"的具体位置信息呢?
我们试试看,这次保留标记路径里面其他全部信息,只修改"p"这一点。
sel = 'body > div.note > div.post > div.article > div.show-content > div > p > a'
再次运行我们的函数:
print(get_text_link_from_sel(sel))
这是输出结果:
好了,我们要找的内容,全都在这儿了。
但是,我们的工作还没完。
我们还得把采集到的信息输出到Excel中保存起来。
还记得我们常用的数据框工具 Pandas 吗?又该让它大显神通了。
import pandas as pd
只需要这一行命令,我们就能把刚才的列表变成数据框:
df = pd.DataFrame(get_text_link_from_sel(sel))
让我们看看数据框内容:
df
内容没问题,不过我们对表头不大满意,得更换为更有意义的列名称:
df.columns = ['text', 'link']
再看看数据框内容:
df
好了,下面就可以把抓取的内容输出到Excel中了。
Pandas内置的命令,就可以把数据框变成csv格式,这种格式可以用Excel直接打开查看。
df.to_csv('output.csv', encoding='gbk', index=False)
注意这里需要指定encoding(编码)为gbk,否则默认的utf-8编码在Excel中查看的时候,有可能是乱码。
我们看看最终生成的csv文件吧。
成功!
以下是之前自己的一些实践(简单易懂):
1.ctf中的简单爬虫题
方法一:
import requests,regex
nextUrl = base = 'https://hgame-spider.vidar.club/xxxx'
while 1:
keys = regex.findall('<a href=\"(\S+)\">点我试试</a>',requests.get(nextUrl).text)
if len(keys) == 0: break
nextUrl = base + keys[0]
print(nextUrl)
print(requests.get(nextUrl).headers)
把头(base)确定,获取“点我试试”按钮的内容,拼接,直到不再跳转之时(这是官方wp解法
方法二:(思想是一样的,不过更加花哨一点
# coding=utf-8
from cgitb import text
import requests
from bs4 import BeautifulSoup
import re
def xhtml( stra,num = 0 ):
flag_num = 1
strHtml = requests.get(stra) # Get方式获取网页数据
html = strHtml.text #结果转换为文本格式
soup=BeautifulSoup(html,"html.parser")#转换成xml格式方便提取,这个用法不常见,需要百度其用法
if html.count("你现在在") == 0:#或者("flag")==2 #或者长度不为特定1-x天页面字数固定某几个长度(有一点猜测的成分,但是多试几次就好了,实现方法很多。这里代码的意思是当前页面没有了你现在在这四个字。
flag_num = 2
global keykkK
for k in soup.find_all('a'): #a标签,超链接(打开源代码可以看到有几个点我试试就有几个a标签,而每次都只有一个a标签里面有内容,即?key=……,那么我们把它取下来和前面的url进行拼接
num = num + 1 # 用于验证
if len(k['href']) != 0:
keykkK = k['href'] #直接获取a标签href属性值
# 查找flag实现 待实现 之后将flag带到while中判断
return flag_num ,keykkK,num
# 返回 flag标记、url、当前关卡数
uflag = 1 # flag 初始化
key = 0 #验证 第几个页面
a = 0 #验证 第几关
straa = 'https://hgame-spider.vidar.club/ba357f7c0a'
while uflag == 1 :#直到出现目标停止循环
if key == a:
key += 1 #相等就证明在运行正常
resu = xhtml(straa)
#获取返回值
print("第"+ str(resu[2]) +"关")
a = resu[2]#当前页面几个[点我试试]
uflag = resu[0]#获取标志参数情况
kk = resu[1] #key值,进行url拼接
straa = 'https://hgame-spider.vidar.club/ba357f7c0a' #强制重置URL
straa = straa + kk
print(straa)
2.普通爬虫开端
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import cookielib
import urllib2
url = "http://www.baidu.com"
response1 = urllib2.urlopen(url)
print
"第一种方法"
# 获取状态码,200表示成功
print
response1.getcode()
# 获取网页内容的长度
print
len(response1.read())
print
"第二种方法"
request = urllib2.Request(url)
# 模拟Mozilla浏览器进行爬虫
request.add_header("user-agent", "Mozilla/5.0")
response2 = urllib2.urlopen(request)
print
response2.getcode()
print
len(response2.read())
print
"第三种方法"
cookie = cookielib.CookieJar()
# 加入urllib2处理cookie的能力
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
urllib2.install_opener(opener)
response3 = urllib2.urlopen(url)
print
response3.getcode()
print
len(response3.read())
print
cookie
# -*- coding: utf-8 -*-
import urllib.request
import urllib
# 1、网址url --百度
url = 'http://www.baidu.com'
# 2、创建request请求对象
request = urllib.request.Request(url)
# 3、发送请求获取结果
response = urllib.request.urlopen(request)
htmldata = response.read()
# 4、设置编码方式
htmldata = htmldata.decode('utf-8')
# 5、打印结果
print(htmldata)
# 6、打印爬去网页的各类信息
print("response的类型:", type(response))
print("请求的url:", response.geturl())
print("响应的信息:", response.info())
print("状态码:", response.getcode())
# 7、爬取数据保存到文件
fileOb = open('baidu.txt', 'w', encoding='utf-8') # 打开一个文件,没有就新建一个
fileOb.write(htmldata)
fileOb.close()
运行一下:
下个学期快开学了,下个阶段的目标准备学习python脚本