requests和BeautifulSoup模塊的使用


  用python寫爬蟲時,有兩個很好用第三方模塊requests庫和beautifulsoup庫,簡單學習了下模塊用法:

1,requests模塊

  Python標准庫中提供了:urllib、urllib2、httplib等模塊以供Http請求,使用起來較為麻煩。requests是基於Python開發的HTTP 第三方庫,在Python內置模塊的基礎上進行了高度的封裝,使用了更簡單,代碼量更少。 官方文檔:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

  requests的api 主要包括了八個方法:

def get(url, params=None, **kwargs):
def options(url, **kwargs):
def head(url, **kwargs):
def post(url, data=None, json=None, **kwargs):
def put(url, data=None, **kwargs):
def patch(url, data=None, **kwargs):
def delete(url, **kwargs):

#上面方法都是基於request方法實現的(method參數)
def request(method, url, **kwargs):

  最常用的主要是get方法和post方法,其源碼如下,都是基於request方法,參數和request方法一樣。

def get(url, params=None, **kwargs):
    """Sends a GET request.  
    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """
    kwargs.setdefault('allow_redirects', True)
    return request('get', url, params=params, **kwargs)   # 發送get請求,基於request方法,method=‘get’

def post(url, data=None, json=None, **kwargs):
    """Sends a POST request.

    :param url: URL for the new :class:`Request` object.
    :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
    :param json: (optional) json data to send in the body of the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """
    return request('post', url, data=data, json=json, **kwargs)  # 發送post請求,基於request方法,method=‘post‘’

  request方法源碼如下:

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    :param method: method for the new :class:`Request` object.     #method,對應‘get’,‘post’,‘put’,'delete'等。必須參數
    :param url: URL for the new :class:`Request` object.       # url,必須參數
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.      # params,url中的查詢字符竄,字典或字節類型,urlencode方法
    :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.  #data, 發送的數據,字典,字節,和類文件對象
    :param json: (optional) json data to send in the body of the :class:`Request`.                   #json, 發送的數據,json格式的 
    :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.              # headers,請求頭,字典格式
    :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.                # cookies,字典或CookieJar對象
    :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. #字典{‘name’:file-like obj}
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``           #或字典{‘name’:file-tuple} (嵌套元組)
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
        defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
        to add for the file.
    :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.         #auth,元組
    :param timeout: (optional) How long to wait for the server to send data        #超時時間,浮點數或元組
        before giving up, as a float, or a :ref:`(connect timeout, read
        timeout) <timeouts>` tuple.
      :type timeout: float or tuple
    :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.  #allow_redirects,是否允許重定向,
      :type allow_redirects: bool
    :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.   #代理服務器,協議和url字典 {'http':proxy_ip}
    :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to ``True``.   #verify,是否ssl認證,默認為True
    :param stream: (optional) if ``False``, the response content will be immediately downloaded.    # stream,默認為false,會直接下載到內存,文件較大時應設置為True
    :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
    :return: :class:`Response <Response>` object
      :rtype: requests.Response

    Usage::

      >>> import requests
      >>> req = requests.request('GET', 'http://httpbin.org/get')
      <Response [200]>
    """

    # By using the 'with' statement we are sure the session is closed, thus we
    # avoid leaving sockets open which can trigger a ResourceWarning in some
    # cases, and look like a memory leak in others.
    with sessions.Session() as session:
        return session.request(method=method, url=url, **kwargs)

  相關參數注意:data數據類型可以為字典,但若是嵌套字典時需要用json。參數舉例如下:

method:
    # requests.request(method='get', url='http://127.0.0.1:8000/test/')
    # requests.request(method='post', url='http://127.0.0.1:8000/test/')
params:
    # - 可以是字典
    # - 可以是字符串
    # - 可以是字節(ascii編碼以內)

    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params={'k1': 'v1', 'k2': '水電費'})

    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params="k1=v1&k2=水電費&k3=v3&k3=vv3")

    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params=bytes("k1=v1&k2=k2&k3=v3&k3=vv3", encoding='utf8'))

    # 錯誤
    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params=bytes("k1=v1&k2=水電費&k3=v3&k3=vv3", encoding='utf8'))
data:
    # 可以是字典
    # 可以是字符串
    # 可以是字節
    # 可以是文件對象

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data={'k1': 'v1', 'k2': '水電費'})

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data="k1=v1; k2=v2; k3=v3; k3=v4"
    # )

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data="k1=v1;k2=v2;k3=v3;k3=v4",
    # headers={'Content-Type': 'application/x-www-form-urlencoded'}
    # )

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data=open('data_file.py', mode='r', encoding='utf-8'), # 文件內容是:k1=v1;k2=v2;k3=v3;k3=v4
    # headers={'Content-Type': 'application/x-www-form-urlencoded'}
    # )
json:
    # 將json中對應的數據進行序列化成一個字符串,json.dumps(...)
    # 然后發送到服務器端的body中,並且Content-Type是 {'Content-Type': 'application/json'}
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     json={'k1': 'v1', 'k2': '水電費'})

headers:
    # 發送請求頭到服務器端
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     json={'k1': 'v1', 'k2': '水電費'},
                     headers={'Content-Type': 'application/x-www-form-urlencoded'}
                     )
cookies():
    # 發送Cookie到服務器端
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     data={'k1': 'v1', 'k2': 'v2'},
                     cookies={'cook1': 'value1'},
                     )
    # 也可以使用CookieJar(字典形式就是在此基礎上封裝)
    from http.cookiejar import CookieJar
    from http.cookiejar import Cookie
    obj = CookieJar()
    obj.set_cookie(Cookie(version=0, name='c1', value='v1', port=None, domain='', path='/', secure=False, expires=None,
                          discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False,
                          port_specified=False, domain_specified=False, domain_initial_dot=False, path_specified=False)
                   )
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     data={'k1': 'v1', 'k2': 'v2'},
                     cookies=obj)
files:
    # 發送文件
    # file_dict = {
    # 'f1': open('readme', 'rb')
    # }
    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # files=file_dict)

    # 發送文件,定制文件名
    # file_dict = {
    # 'f1': ('test.txt', open('readme', 'rb'))
    # }
    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # files=file_dict)

    # 發送文件,定制文件名
    # file_dict = {
    # 'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf")
    # }
    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # files=file_dict)

    # 發送文件,定制文件名
    # file_dict = {
    #     'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf", 'application/text', {'k1': '0'})
    # }
    # requests.request(method='POST',
    #                  url='http://127.0.0.1:8000/test/',
    #                  files=file_dict)

auth:  認證方法
    from requests.auth import HTTPBasicAuth, HTTPDigestAuth
    ret = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('wupeiqi', 'sdfasdfasdf'))
    print(ret.text)

    # ret = requests.get('http://192.168.1.1',
    # auth=HTTPBasicAuth('admin', 'admin'))
    # ret.encoding = 'gbk'
    # print(ret.text)

    # ret = requests.get('http://httpbin.org/digest-auth/auth/user/pass', auth=HTTPDigestAuth('user', 'pass'))
    # print(ret)
   timeout: 超時時間
    # ret = requests.get('http://google.com/', timeout=1)
    # print(ret)

    # ret = requests.get('http://google.com/', timeout=(5, 1))
    # print(ret)

allow_redirects:
    ret = requests.get('http://127.0.0.1:8000/test/', allow_redirects=False)
    print(ret.text)

proxies:
    # proxies = {
    # "http": "61.172.249.96:80",
    # "https": "http://61.185.219.126:3128",
    # }

    # proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

    # ret = requests.get("http://www.proxy360.cn/Proxy", proxies=proxies)
    # print(ret.headers)

    # from requests.auth import HTTPProxyAuth
    #
    # proxyDict = {
    # 'http': '77.75.105.165',
    # 'https': '77.75.105.165'
    # }
    # auth = HTTPProxyAuth('username', 'mypassword')
    #
    # r = requests.get("http://www.google.com", proxies=proxyDict, auth=auth)
    # print(r.text)
stream: ret = requests.get('http://127.0.0.1:8000/test/', stream=True) #默認為false,會直接將文件下載到內存,文件過大時會撐滿內存, print(ret.content) ret.close() # from contextlib import closing # with closing(requests.get('http://httpbin.org/get', stream=True)) as r: # # 在此處理響應。 # for i in r.iter_content(): # 設置成True時,遍歷內容時才開始下載 # print(i)

  request方法的最后調用了Session 類,其內部也實現了request,get,post等方法,部分源碼如下:

class Session(SessionRedirectMixin):
    """A Requests session.
    Provides cookie persistence, connection-pooling, and configuration.
    Basic Usage::

      >>> import requests
      >>> s = requests.Session()
      >>> s.get('http://httpbin.org/get')
      <Response [200]>

    Or as a context manager::

      >>> with requests.Session() as s:
      >>>     s.get('http://httpbin.org/get')
      <Response [200]>

 1.1 Seeeion 對象 

  下面代碼兩者的區別:requests.get相當於每次請求時都新建了一個session對象,而requests.session()是新建一個session對象,然后重復利用該session對象,從而實現保持session對象的cookie,參數等在不同請求中保持持久化。(所以Session對象擁有requests的所有http method)

       官方文檔:http://docs.python-requests.org/en/latest/user/advanced/#session-objects

  參考博客:https://stackabuse.com/the-python-requests-module/

#利用Session
client = requests.session() resp = client.get(url='...')
#利用requests resp
= requests.get(url='...')

  不同session的cookie保持:如下面的代碼,對於first_session每次請求都會帶上{"cookies":{"cookieone":"111"}}, 而對於second_session,每次請求都會帶上{"cookies":{"cookietwo":"222"}}

import requests

first_session = requests.Session()  
second_session = requests.Session()

first_session.get('http://httpbin.org/cookies/set/cookieone/111')  
r = first_session.get('http://httpbin.org/cookies')  
print(r.text)

second_session.get('http://httpbin.org/cookies/set/cookietwo/222')  
r = second_session.get('http://httpbin.org/cookies')  
print(r.text)

r = first_session.get('http://httpbin.org/anything')  
print(r.text) 

output:

{"cookies":{"cookieone":"111"}}

{"cookies":{"cookietwo":"222"}}

{"args":{},"data":"","files":{},"form":{},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate","Connection":"close","Cookie":"cookieone=111","Host":"httpbin.org","User-Agent":"python-requests/2.9.1"},"json":null,"method":"GET","origin":"103.9.74.222","url":"http://httpbin.org/anything"}

  session的cookie更新: 如下面代碼中,通過first_session.cookies更新的cookie會跟隨每次請求,而first_session.get() 請求中cookies參數傳入的cookie,只對該請求有效,不會被持久化。

import requests

first_session = requests.Session()

first_session.cookies.update({'default_cookie': 'default'})

r = first_session.get('http://httpbin.org/cookies', cookies={'first-cookie': '111'})  
print(r.text)

r = first_session.get('http://httpbin.org/cookies')  
print(r.text) 

output:

{"cookies":{"default_cookie":"default","first-cookie":"111"}}

{"cookies":{"default_cookie":"default"}}

  session應用舉例:

def requests_session():
    import requests

    session = requests.Session()

    ### 1、首先登陸任何頁面,獲取cookie

    i1 = session.get(url="http://dig.chouti.com/help/service")

    ### 2、用戶登陸,攜帶上一次的cookie,后台對cookie中的 gpsd 進行授權
    i2 = session.post(
        url="http://dig.chouti.com/login",
        data={
            'phone': "8615131255089",
            'password': "xxxxxx",
            'oneMonth': ""
        }
    )
  # 3,保持會話,自動帶着授權的cookie進行訪問
    i3 = session.post(
        url="http://dig.chouti.com/link/vote?linksId=8589623",
    )
    print(i3.text)

  1.2 Response

    request的返回值為Response對象,其有很多有用的屬性和方法,如下:

    通過response.cookies,response.headers,response.status_code,encoding可以拿到服務器返回的cookies, 響應頭,狀態碼,編碼等信息。

    通過response.content和text,可以分別拿到響應網頁的二進制和unicode數據。

class Response(object):
    """The :class:`Response <Response>` object, which contains a
    server's response to an HTTP request.
    """
    __attrs__ = [
        '_content', 'status_code', 'headers', 'url', 'history',
        'encoding', 'reason', 'cookies', 'elapsed', 'request'
    ]
  @property
  def content(self): """Content of the response, in bytes."""
  @property
  def text(self):   """Content of the response, in unicode."""


    另外下載文件時的官方推薦寫法如下,stream=True表示采用數據流,邊下載邊寫入,而不是一次性全部寫入內存,r.iter_content(chunk_size=256)表示每次下載256字節數據。

import requests

r = requests.get('https://cdn.pixabay.com/photo/2018/07/05/02/50/sun-hat-3517443_1280.jpg', stream=True)  
downloaded_file = open("sun-hat.jpg", "wb")  
for chunk in r.iter_content(chunk_size=256):  
    if chunk:
        downloaded_file.write(chunk)

#下面方法能拿到原始的數據
import requests r = requests.get("http://exampleurl.com", stream=True) r.raw

 

 

 

2,BeautifulSoup模塊

  BeautifulSopu模塊是一個可以從HTML或XML文件中提取數據的Python第三方庫。其接受一個html或xml字符串(或html,xml文檔句柄),將文檔被轉換成Unicode,利用解析器來解析這段文檔。BeautifulSoup支持幾種不同的解析器:python標准庫中的html.parser,以及第三方庫lxml,lxml-xml和html5lib。Beautiful Soup最終將復雜HTML文檔轉換成一個復雜的樹形結構,每個節點都是Python對象,所有對象可以歸納為4種: Tag , NavigableString , BeautifulSoup , Comment .

官方文檔:https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/

BeautifulSoup的構造方法接受html文檔后,得到實例化BeautifulSoup對象,由於該對象繼承了Tag類,擁有Tag類的屬性和方法。Beautiful部分源碼:

class BeautifulSoup(Tag):
    ROOT_TAG_NAME = u'[document]'
    DEFAULT_BUILDER_FEATURES = ['html', 'fast']
    ASCII_SPACES = '\x20\x0a\x09\x0c\x0d'
    NO_PARSER_SPECIFIED_WARNING = "No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n\nThe code that caused this warning is on line %(line_number)s of the file %(filename)s. To get rid of this warning, change code that looks like this:\n\n BeautifulSoup([your markup])\n\nto this:\n\n BeautifulSoup([your markup], \"%(parser)s\")\n"
    def __init__(self, markup="", features=None, builder=None,
                 parse_only=None, from_encoding=None, exclude_encodings=None,
                 **kwargs):
        """The Soup object is initialized as the 'root tag', and the
        provided markup (which can be a string or a file-like object)
        is fed into the underlying parser."""

Tag對象與XML或HTML原生文檔中的tag相同,Tag類中有很多方法和屬性來遍歷html文檔中節點和屬性:

html_doc = """
<html><head><title>The Dormouse's story</title></head>
    <body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')

  對於上面的BeautifulSoup對象:

name, 標簽名字: # tag = soup.find('a')
# name = tag.name # 獲取
# print(name)
# tag.name = 'span' # 設置
# print(soup)
# soup.head   #拿到head標簽
attrs, 標簽屬性 # tag = soup.find('a')
# attrs = tag.attrs    # 獲取
# print(attrs)
# tag.attrs = {'ik':123} # 設置
# tag.attrs['id'] = 'iiiii' # 設置
# print(soup)
#tag['id'] #直接拿到屬性
children, 所有子標簽,返回生成器 contents,所有子標簽,返回列表
parent,父節點
next_sibling,下一個兄弟節點
previous_sibling,上一個兄弟節點
# body = soup.find('body')
#
v = body.children #
v = body.contents[0]
decendants, 所有的子孫節點
parents,所有父輩節點
next_siblings,下面所有兄弟節點
previous_siblings,上面所有兄弟節點

# body = soup.find('body')
#
v = body.descendants
string: tag只有一個 NavigableString 類型子節點,那么這個tag可以使用 .string 得到子節點 (NavigableString,類似一個unicode字符竄,string拿到文本) strings: tag中包含多個字符串 [2] ,可以使用 .strings 來循環獲取 stripped_strings: 輸出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白內容:
# tag = soup.find('a')
#tag.string
#for string in tag.strings: # print(repr(string))
clear(),將標簽的所有子標簽全部清空(保留標簽名# tag = soup.find('body')
# tag.clear()
decompose(), 遞歸的刪除所有的標簽(不保留標簽名) # body = soup.find('body')
# body.decompose()
extract(),遞歸的刪除所有的標簽,並獲取刪除的標簽 # body = soup.find('body')
# v = body.extract()
decode,轉換數據為字符串(含當前標簽);decode_contents(不含當前標簽) # body = soup.find('body')
# v = body.decode()
# v = body.decode_contents()
# print(v)
def decode(self, indent_level=None,eventual_encoding=DEFAULT_OUTPUT_ENCODING, formatter="minimal"): """Returns a Unicode representation of this tag and its contents.
默認encoding=‘utf-8’
encode,轉換為字節(含當前標簽);encode_contents(不含當前標簽) # body = soup.find('body')
# v = body.encode()
# v = body.encode_contents()
# print(v)
def encode(self, encoding=DEFAULT_OUTPUT_ENCODING,indent_level=None, formatter="minimal",errors="xmlcharrefreplace"): 默認encoding=‘utf-8’    

find_all() :搜索當前tag的所有tag子節點,獲取匹配的所有標簽,以列表形式返回 def find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs):
        """Extracts a list of Tag objects that match the given
        criteria.  You can specify the name of the Tag and any
        attributes you want the Tag to have.
        The value of a key-value pair in the 'attrs' map can be a
        string, a list of strings, a regular expression object, or a
        callable that takes a string and returns whether or not the
        string matches for some custom definition of 'matches'. The
        same is true of the tag name."""
name:查找所有名字為 name 的tag   (name可以為字符串,正則表達式,列表,方法,True) #True匹配任意標簽名 # tags = soup.find_all('a')
# print(tags)
 
# tags = soup.find_all('a',limit=1)   # limit,只匹配一次;類似於find()
# print(tags)

attrs參數:tag的屬性值包含篩選條件
# tags = soup.find_all(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie')
# # tags = soup.find(name='a', class_='sister', recursive=True, text='Lacie')
soup.find_all("a", class_="sister")
# print(tags)

# ####### 列表 #######
# v = soup.find_all(name=['a','div'])
# print(v) 
# v = soup.find_all(class_=['sister0', 'sister'])   #class 為python關鍵字,所以加下划線
# print(v) 
# v = soup.find_all(text=['Tillie'])
# print(v, type(v[0]))  
# v = soup.find_all(id=['link1','link2'])
# print(v) 
# v = soup.find_all(href=['link1','link2'])
# print(v)

# ####### 正則 ####### import re # rep = re.compile('p') # rep = re.compile('^p') # v = soup.find_all(name=rep) # print(v) # rep = re.compile('sister.*') # v = soup.find_all(class_=rep) # print(v) # rep = re.compile('http://www.oldboy.com/static/.*') # v = soup.find_all(href=rep) # print(v) # ####### 方法篩選 ####### # def func(tag): # return tag.has_attr('class') and tag.has_attr('id') # v = soup.find_all(name=func) # print(v
find(),獲取匹配的第一個標簽 # tag = soup.find('a')
# print(tag)
# tag = soup.find(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie')
# tag = soup.find(name='a', class_='sister', recursive=True, text='Lacie')
# print(tag)
get(),獲取標簽屬性
def get(self, key, default=None):
return self.attrs.get(key, default)
# tag = soup.find('a') 
#
v = tag.get('id')
#類似於tag.attrs['id']
# print(v)
has_attr(),檢查標簽是否具有該屬性 # tag = soup.find('a')
# v = tag.has_attr('id')
# print(v)
def has_attr(self, key):
        return key in self.attrs
get_text(),獲取標簽內部文本內容  #類似string
# tag = soup.find('a')
# v = tag.get_text('id')
# print(v)
 index(),檢查標簽在某標簽中的索引位置 def index(self, element):
        """
        Find the index of a child by identity, not value. Avoids issues with
        tag.contents.index(element) getting the index of equal elements.
        """
        for i, child in enumerate(self.contents):
            if child is element:
                return i
        raise ValueError("Tag.index: element not in tag")
# tag = soup.find('body')
# v = tag.index(tag.find('div'))
# print(v)
 
is_empty_element(),是否是空標簽(是否可以是空)或者自閉合標簽,
  判斷是否是如下標簽:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame', 'base'
# tag = soup.find('br')
# v = tag.is_empty_element
# print(v)
select,select_one, CSS選擇器  (和css選擇器一樣)
soup.select("title") 
soup.select("p nth-of-type(3)")   #父元素中第三個p標簽
soup.select("body a") 
soup.select("html head title") 
tag = soup.select("span,a") 
soup.select("head > title") 
soup.select("p > a") 
soup.select("p > a:nth-of-type(2)") 
soup.select("p > #link1") 
soup.select("body > a") 
soup.select("#link1 ~ .sister")
soup.select("#link1 + .sister") 
soup.select(".sister")
soup.select("[class~=sister]") 
soup.select("#link1") 
soup.select("a#link2") 
soup.select('a[href]') 
soup.select('a[href="http://example.com/elsie"]')
soup.select('a[href^="http://example.com/"]') 
soup.select('a[href$="tillie"]')
soup.select('a[href*=".com/el"]') 
 
from bs4.element import Tag
def default_candidate_generator(tag):
    for child in tag.descendants:
        if not isinstance(child, Tag):
            continue
        if not child.has_attr('href'):
            continue
        yield child 
tags = soup.find('body').select("a", _candidate_generator=default_candidate_generator)
print(type(tags), tags)
 
from bs4.element import Tag def default_candidate_generator(tag): for child in tag.descendants: if not isinstance(child, Tag): continue if not child.has_attr('href'): continue yield child tags = soup.find('body').select("a", _candidate_generator=default_candidate_generator, limit=1) print(type(tags), tags)
修改文檔樹標簽的內容
# tag = soup.find('span')
# print(tag.string)          # 獲取
# tag.string = 'new content' # 設置
# print(soup)
 
# tag = soup.find('body')
# print(tag.string)
# tag.string = 'xxx'
# print(soup)
 
# tag = soup.find('body')
# v = tag.stripped_strings  # 遞歸內部獲取所有標簽的文本
# print(v)

append():在當前標簽內部追加一個標簽
  # tag = soup.find('body')
  # tag.append(soup.find('a'))
  # print(soup)
 
  # from bs4.element import Tag
  # obj = Tag(name='i',attrs={'id': 'it'})
  # obj.string = '我是一個新來的'
  # tag = soup.find('body')
  # tag.append(obj)
  # print(soup)
  
insert():指定位置插入標簽 
  # tag = soup.find('body')
  # tag.insert(2, obj)
  # print(soup)
insert_after(),insert_before() 在當前標簽后面或前面插入
replace_with()當前標簽替換為指定標簽
 
創建標簽之間的關系
# tag = soup.find('div')
# a = soup.find('a')
# tag.setup(previous_sibling=a)
# print(tag.previous_sibling)
 
wrap,將指定標簽把當前標簽包裹起來
# tag = soup.find('a')
# v = tag.wrap(soup.find('p'))  #a包裹p
# print(soup)
 
unwrap,去掉當前標簽,將保留其包裹的標簽
# tag = soup.find('a')
# v = tag.unwrap()    # a包裹的標簽
# print(soup)
 

示例:使用BeautifulSoup模塊解析當前網頁,並提取出所有鏈接屬性和文本內容,代碼如下:

#coding:utf-8
import requestsfrom bs4 import BeautifulSoup #下載當前網頁html文件 response = requests.get("https://www.cnblogs.com/silence-cho/p/9786069.html") print type(response.text) with open('python.html','w') as f: f.write(response.text.encode('utf-8')) with open('python.html','r') as f: html_file = f.read().decode('utf-8') #使用Beautiful模塊 soup = BeautifulSoup(html_file,'lxml') a_tags = soup.find_all('a') for a_tag in a_tags: if a_tag.has_attr('href'): print a_tag.attrs['href'] text = soup.get_text().encode('gbk',errors='ignore') #使用get_text()方法,拿到所有文本 with open('text1.txt','w') as f: f.write(text) strings = soup.strings #使用strings屬性,拿到所有文本 with open('string.txt','w') as f: for string in strings: #strings 為generator類型,包含拿到的所有文本 f.write(string.encode('gbk',errors='ignore'))

3,爬蟲應用

登錄抽屜

'''
自動登錄抽屜熱搜榜流程:先訪問主頁,獲取cookie1,然后攜帶用戶名,密碼和cookie1訪問登陸頁面對cookie1授權,隨后就能利用cookie1直接訪問個人主頁等。
注意真正起作用的是cookie1里面gpsd': '2c805bc26ead2dfcc09ef738249abf65,第二次進行登陸時對這個值進行了認證,
隨后就能利用cookie1進行訪問了,進行登錄時也會返回cookie2,但cookie2並不起作用
'''

import requests
from bs4 import BeautifulSoup

#訪問首頁
response=requests.get(
    url="https://dig.chouti.com/",
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:62.0) Gecko/20100101 Firefox/62.0"}
)
cookie_dict = response.cookies.get_dict()
print cookie_dict

#登錄頁面,發送post
response2= requests.post(
    url="https://dig.chouti.com/login",
    data={
        "oneMonth":"1",
        "password":"你自己的密碼",
        "phone":"8618626429847",
    },
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:62.0) Gecko/20100101 Firefox/62.0"},
    cookies=cookie_dict,
)

#攜帶cookie,訪問首頁,顯示為登錄狀態
response3= requests.get(
    url="https://dig.chouti.com/",
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:62.0) Gecko/20100101 Firefox/62.0"},
    cookies = cookie_dict
)

#攜帶cookie,進行點贊,返回推送成功
response4 = requests.post(
    url="https://dig.chouti.com/link/vote?linksId=22650731",
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:62.0) Gecko/20100101 Firefox/62.0"},
    cookies = cookie_dict
)
print response4.text
#{"result":{"code":"9999", "message":"推薦成功", "data":{"jid":"cdu_53961215992","likedTime":"1539697099953000","lvCount":"13","nick":"silence624","uvCount":"1","voteTime":"小於1分鍾前"}}}
登陸抽屜熱搜榜

登陸github

import requests
from bs4 import BeautifulSoup
response1 = requests.get(
    url="https://github.com/login",   #url為https://github.com/時拿到的cookie不行
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:62.0) Gecko/20100101 Firefox/62.0"},

)
cookie_dict = response1.cookies.get_dict()  #拿到cookie
print cookie_dict
soup = BeautifulSoup(response1.text,features='html.parser')
tag = soup.find(name='input',attrs={"name":"authenticity_token"})
authenticity_token = tag.attrs.get('value')    # 從前端頁面拿到跨站偽造請求token值
print authenticity_token
response = requests.post(
    url='https://github.com/session',
    data={
        "authenticity_token":authenticity_token,
        "commit":"Sign+in",
        "login":"xxx",
        "password":"xxx",
        "utf8":""
    },
    headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; rv:62.0) Gecko/20100101 Firefox/62.0"},
    cookies = cookie_dict,
)
# print response.text
c2=response.cookies.get_dict()
cookie_dict.update(c2)    #自動登錄,對cookie值進行更新

r = requests.get(url="https://github.com/settings/repositories",cookies=cookie_dict)   #利用更新后的cookie保持會話,拿到倉庫名
soup2 = BeautifulSoup(r.text,features='html.parser')
tags = soup2.find_all(name='a',attrs={'class':'mr-1'})
for item in tags:
    print item.get_text()
登陸github

 

 

 

 

 參考博客:http://www.cnblogs.com/wupeiqi/articles/6283017.html

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM