记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 源码确定新闻标题信息组织形式。

QQ图片20220218173305

目标信息存在于 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套装……(省略一万步

b219496f01eab2eb8f5f737b7662636b

代码部分:

读入网页加以解析抓取,需要用到的软件包是 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)

这就是获得的结果了:
0d2fdef3e004cbe69f99cf0

我们心里有数了。取回来的网页信息是正确的,内容是完整的。

好了,我们来看看怎么趋近自己的目标吧。

我们先用简单粗暴的方法,尝试获得网页中包含的全部链接。

把返回的内容作为HTML文件类型,我们查看 links 属性:

r.html.links

这是返回的结果:

fea307f008e789fbe7b351f6a7329ec2

这里许多链接,看似都不完全。例如第一条结果,只有:

'/'

这是什么东西?是不是链接抓取错误啊?

不是,这种看着不像链接的东西,叫做相对链接。它是某个链接,相对于我们采集的网页所在域名(https://www.jianshu.com)的路径。

这就好像我们在国内邮寄快递包裹,填单子的时候一般会写“XX省XX市……”,前面不需要加上国家名称。只有国际快递,才需要写上国名。

但是如果我们希望获得全部可以直接访问的链接,怎么办呢?

很容易,也只需要一条 Python 语句。

r.html.absolute_links

这里,我们要的是“绝对”链接,于是我们就会获得下面的结果:

3fc66e785b9293f833d79fb223c59daf

这回看着是不是就舒服多了?

我们的任务已经完成了吧?链接不是都在这里吗?

链接确实都在这里了,可是跟我们的目标是不是有区别呢?

检查一下,确实有。

我们不光要找到链接,还得找到链接对应的描述文字呢,结果里包含吗?

没有。

结果列表中的链接,都是我们需要的吗?

不是。看长度,我们就能感觉出许多链接并不是文中描述其他数据科学文章的网址。

这种简单粗暴直接罗列HTML文件中所有链接的方法,对本任务行不通。

那么我们该怎么办?

我们得学会跟 Python 说清楚我们要找的东西。这是网页抓取的关键。

想想看,如果你想让助手(人类)帮你做这事儿,怎么办?

你会告诉他:

“寻找正文中全部可以点击的蓝色文字链接,拷贝文字到Excel表格,然后右键复制对应的链接,也拷贝到Excel表格。每个链接在Excel占一行,文字和链接各占一个单元格。”

虽然这个操作执行起来麻烦,但是助手听懂后,就能帮你执行。

同样的描述,你试试说给电脑听……不好意思,它不理解。

因为你和助手看到的网页,是这个样子的。

7a4cce27eb5ca89bac13e9f5e20efc11

电脑看到的网页,是这个样子的。

f792567a19ec26ab52037a2de5b2b2be

为了让你看得清楚源代码,浏览器还特意对不同类型的数据用了颜色区分,对行做了编号。

数据显示给电脑时,上述辅助可视功能是没有的。它只能看见一串串字符。

那可怎么办?

仔细观察,你会发现这些HTML源代码里面,文字、图片链接内容前后,都会有一些被尖括号括起来的部分,这就叫做“标记”。

所谓HTML,就是一种标记语言(超文本标记语言,HyperText Markup Language)。

标记的作用是什么?它可以把整个的文件分解出层次来。

857a411ef5622d8f07fce66999c20aa0

如同你要发送包裹给某个人,可以按照“省-市-区-街道-小区-门牌”这样的结构来写地址,快递员也可以根据这个地址找到收件人。

同样,我们对网页中某些特定内容感兴趣,可以依据这些标记的结构,顺藤摸瓜找出来。

这是不是意味着,你必须先学会HTML和CSS,才能进行网页内容抓取呢?

不是的,我们可以借助工具,帮你显著简化任务复杂度。

这个工具,Google Chrome浏览器自带。

我们在样例文章页面上,点击鼠标右键,在出现的菜单里面选择“检查”。

48ecada542f78292ca186e7ded861cfa

这时,屏幕下方就会出现一个分栏。

47abd74b19570b397512c74b2d02b4c5

我们点击这个分栏左上角(上图红色标出)的按钮。然后把鼠标悬停在第一个文内链接(《玉树芝兰》)上面,点击一下。

cb3027a6f4d09cdf6ab1ea0afad086ef

此时,你会发现下方分栏里面,内容也发生了变化。这个链接对应的源代码被放在分栏区域正中,高亮显示。

953c64715d62d97191e7b7a5cc537f36

确认该区域就是我们要找的链接和文字描述后,我们鼠标右键选择高亮区域,并且在弹出的菜单中,选择 Copy -> Copy selector。

d0263745e29fc1b0e153986dd0738052

找一个文本编辑器,执行粘贴,就可以看见我们究竟复制下来了什么内容。

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')]

没问题,对吧?

好,我们试试看第二个链接。

我们还是用刚才的方法,使用下面分栏左上角的按钮点击第二个链接。

1f8be916b59bd5be4cd937c4eaa3316d

下方出现的高亮内容就发生了变化:

e44260963632ec2b5016c3ea4e9a2e99

我们还是用鼠标右键点击高亮部分,拷贝出 selector。

577478bc482c6c45b5479bd1a5472589

然后我们直接把获得的标记路径写到 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))

这是输出结果:
f5a0fa92a26a720de15351646999381d

好了,我们要找的内容,全都在这儿了。

但是,我们的工作还没完。

我们还得把采集到的信息输出到Excel中保存起来。

还记得我们常用的数据框工具 Pandas 吗?又该让它大显神通了。

import pandas as pd

只需要这一行命令,我们就能把刚才的列表变成数据框:

df = pd.DataFrame(get_text_link_from_sel(sel))

让我们看看数据框内容:

df
d4bb0213ee11d92bbca72f7f4f5f46d5

内容没问题,不过我们对表头不大满意,得更换为更有意义的列名称:

df.columns = ['text', 'link']

再看看数据框内容:

df
351ec004d75cff7193591526681b3745

好了,下面就可以把抓取的内容输出到Excel中了。

Pandas内置的命令,就可以把数据框变成csv格式,这种格式可以用Excel直接打开查看。

df.to_csv('output.csv', encoding='gbk', index=False)

注意这里需要指定encoding(编码)为gbk,否则默认的utf-8编码在Excel中查看的时候,有可能是乱码。

我们看看最终生成的csv文件吧。

9b9804c8bcc6d2b71c0b3a251d47f5cc

成功!

以下是之前自己的一些实践(简单易懂):

1.ctf中的简单爬虫题

9b9804c8bcc6d2b71c0b3a251d47f5cc

方法一:

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()

运行一下:

QQ图片20220218143642 QQ图片20220218143820

下个学期快开学了,下个阶段的目标准备学习python脚本

posted @ 2022-02-18 14:44  时空怡梦笙兮  阅读(188)  评论(0编辑  收藏  举报