鱼C论坛

 找回密码
 立即注册
查看: 2696|回复: 0

[技术交流] python爬虫学习笔记(二)——URL

[复制链接]
发表于 2017-7-13 23:38:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
        做爬虫的时候无外乎就是向目标web服务器发送http请求,然后接受响应,然后解析返回的信息,提取url,再利用获取到的url,再次提交,所以不管怎么说,url是爬虫中比较重要的部分,也是比较基础的部分,我自己感觉学爬虫首先需要懂http协议,而http中url、http请求和 响应格式是比较重要的部分,在前面这几篇中,我想把这些基础概念进行一下说明,这篇讲url,后面就分别是http请求和http的响应
        url简介
        url,全称是Uniform Resource Locator(统一资源定位符),它主要用来标明请求资源所在位置,它的基本格式为:
scheme://hostname[:port] / path / [;parameters][?query]#fragment,各个部分的含义如下:

    scheme:表示网络传输协议,url中一般有http/https/ftp/mailto 等等,这个一般稍微了解网络的都知道,就不再详细的说下去

[list=2]
hostname:主机名,一般是一串字符串,也就是一个域名,也可以是一个IP地址,用来表示目标服务器在网络中的位置,如果是一个域名,在请求时将会由DNS服务器解析成IP地址

[list=3]
port:端口号,在url中这个值可有可无,如果没有则采用协议的默认端口号,比如http的80,ftp的21端口等等

[list=4]
path:请求资源在服务器上的位置,我们通过前面的host找到了对应的主机,加上这个文件路径,表示我们将要请求这台机器上这个位置的内容,服务器接受到请求后将目标文件返回给我们

[list=5]
parameters:这是用于指定特殊参数的可选项。这个基本上很少出现,具体怎么用,我也不太懂

[list=6]
query:请求参数,一般用于给动态网页传递参数,它一般是一个键值对,书写方式为key=value,不同键值对间使用&分割


    fragment:用于指定网络资源中的片断,这样说可能不太好懂,但是这个东西说白了,我感觉它就是定位本页面的锚点(知道html的人应该都知道锚点,不知道的请自行百度)

url分绝对路径和相对路径,绝对路径就上上面的格式,有协议、域名、文件路径等等,这个一般用于请求发起方,毕竟你要请求数据,总的告诉浏览器,你要请求哪台主机,什么位置的数据。这个值在浏览器的地址栏中可以看见,而相对路径一般只有文件路径和后面的部分,它相对于这台主机上来说的,一般在解析网页时出现的比较多,为了能利用这个解析出来的url进行下次请求,我们需要将绝对路径转化为相对路径
下面看一份代码,这段代码是进行url操作的,我将一般的url操作给定义了一个类:
  1. import urlparse
  2. import re

  3. DEFAULT_CODINNG = "utf-8"
  4. class Url:
  5.     def __init__(self, url, encoding = DEFAULT_CODINNG):
  6.         if not re.match(r"http[s]?://", url):
  7.             self.url = "http://" + url #默认协议是http协议
  8.         else:
  9.             self.url = url
  10.         self.encoding = encoding

  11.         urlparam = urlparse.urlparse(self.url) #使用Python自带的模块解析url
  12.         self.protocl = urlparam.scheme
  13.         if urlparam.port is None: #获取url中主机的端口,如果没有,则默认使用80端口
  14.             self.port = 80
  15.         else:
  16.             self.port = int(urlparam.port)

  17.         self.host = urlparam.hostname

  18.         self.query = urlparam.query
  19.         self.fragment = urlparam.fragment
  20.         self.path = urlparam.path
  21.         self.param = urlparam.params

  22.     def getHost(self): #h获取主机名
  23.         return self.host

  24.     def getDomain(self): #获取域名
  25.         return self.host

  26.     def getPort(self): #获取端口
  27.         return self.port

  28.     def getPath(self): #获取url的路经
  29.         return self.path

  30.     def getFileName(self): #获取文件名称
  31.         return self.path[self.path.rfind("/") + 1:]

  32.     def getExt(self): #获取文件扩展名
  33.         return self.path[self.path.rfind(".") + 1:]

  34.     def getFullUrl(self):
  35.         netloc = self.host + ":" + str(self.port)
  36.         data = (self.protocl, netloc, self.path, self.param, self.query, self.fragment)
  37.         _url = urlparse.urlunparse(data)

  38.         un_url = ""
  39.         try:
  40.             un_url = unicode(_url)
  41.         except UnicodeDecodeError, e:
  42.             un_url = unicode(_url, self.encoding, "replace")

  43.         return un_url
  44.     def getQueryString(self):
  45.         return self.query

  46.     def __str__(self):
  47.         return self.getFullUrl()


  48. if __name__ == '__main__':
  49.     url = Url("http://www.anquanbao.com/book/index.php?id=1#top")
  50.     print url.getDomain()
  51.     print url.getExt()
  52.     print url.getFileName()
  53.     print url.getFullUrl()
  54.     print url.getHost()
  55.     print url.getPath()
  56.     print url.getPort()
  57.     print url.getQueryString()
复制代码

这段代码演示了如何解析url的各个部分,并且将url的各个部分拼接成一个完成的url字符串,其实在python中自带的urlparse是一个十分优秀的模块,基本涵盖了url的所有操作,在这提供这段代码主要是为了演示url各个部分的含义,我自己感觉这段使用价值不高。
最后在看一个比较麻烦的问题——url编码问题:
在爬取页面的时候可能会出现url中带有中文,这个时候就需要进行编码的转化,在转化时具体的编码规则我不是太清楚,在使用Python时一般都是将其转化为utf-8编码,然后再调用urllib库中的的函数unquote来进行转化。我们利用这个方法对含有中文的url进行转化:在上面的类中添加这样的代码:

  1.     def urlEncoding(self):
  2.         url_en = self.getFullUrl().encode("utf-8")
  3.         url_en = urllib.quote(url_en)
  4.         return url_en
复制代码

上述代码首先将传入的url转化为Unicode字符串,然后转化为utf-8,接着调用quote函数进行编码,一切都是这么顺理成章,但是我们使用一个带有中文的部分进行测试后发现,它将"http://www.baidu.com?query=测试url:转化成了"http%3A//www.baidu.com%3A80%3Fquery%3D%E6%B5%8B%E8%AF%95url",原来在url中,各个部分除了不能出现中文,还有诸如“:”等一些特殊字符,但是在url中需要它作为格式的区分,函数将它整个进行了转化,既然不能对整体进行转化,那么我们就对各个部分进行转化其实函数quote只需要传入一个字符串,这个字符串不需要满足url的格式,所以,现在又有了一个新的思路,我们使用urlparse将url分成各个部分,然后针对各个部分调用quote转化,最后将它们拼接起来就成了一个完整的url,下面是具体的代码:

  1. def urlEncoding(self):
  2.         #默认scheme、host、port这个几个部分没有中文
  3.         #编码path
  4.         path_en = self.path
  5.         if self.path == "":
  6.             path_en = unicode(self.path, self.encoding, "replace")
  7.             path_en = path_en.encode('utf-8')
  8.             path_en = urllib.quote(path_en)

  9.         #编码fragment
  10.         fragment_en = self.fragment
  11.         if self.fragment != "":
  12.             fragment_en = unicode(self.fragment, self.encoding, "replace")
  13.             fragment_en = fragment_en.encode('utf-8')
  14.             fragment_en = urllib.quote(fragment_en)

  15.         query_en = self.query
  16.         if self.query != "":
  17.             query_en = ""
  18.             query_dict = urlparse.parse_qs(self.query)
  19.             for k, v in query_dict.iteritems():
  20.                 k_en = unicode(k, self.encoding, "replace")
  21.                 k_en = k_en.encode('utf-8')
  22.                 k_en = urllib.quote(k_en)

  23.                 value_en = v[0]
  24.                 value_en = unicode(value_en, self.encoding, "replace")
  25.                 value_en = value_en.encode('utf-8')
  26.                 value_en = urllib.quote(value_en)

  27.                 query_en = query_en + k_en + "=" + value_en + "&"

  28.             query_list = list(query_en)
  29.             if query_list[-1] == "&":
  30.                 query_list.pop()
  31.                 query_en = "".join(query_list)

  32.         netloc = self.host + ":" + str(self.port)

  33.         _url = self.protocl + "://" + netloc
  34.         if self.param != "":
  35.             _url += ";" + self.param
  36.         _url += path_en
  37.         if self.query != "":
  38.             _url += "?" + query_en
  39.         if self.fragment != "":
  40.             _url += "#" + fragment_en

  41.         return _url

复制代码

将这个函数加入到类中,这样就可以进行url的编码,当然在这,异常处理可能不够,毕竟编码问题是最让人头疼的,还有就是我默认向协议、主机、端口、参数这些地方不会出现特殊字符,但是具体是不是这样我也不太清楚,还是那句话,这些代码只是给大家做一个参考。
        好了,url的部分就说到这,下一篇将进行http请求的说明,敬请期待。。。。
       

评分

参与人数 2荣誉 +4 鱼币 +8 收起 理由
康小泡 + 4
小甲鱼 + 4 + 4 支持楼主!

查看全部评分

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-13 03:10

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表