我编写了将Finviz替换为代码的href和附加到代码的href的代码。我测试了多页和一页。我是python编程的新手,因此相信我的代码很糟糕。我希望有人能够帮助我进行审查,并指出改进它的方法。我的目标是使代码具有比现在更高的可伸缩性。

import bs4 as bs
import requests

class finviz_crawler():
    def requesting(self,type):
        items=1
        baseurl='https://finviz.com/screener.ashx?v=111'
        if type == 'sp500':
            firstpage=baseurl+'&f=idx_sp500&o=ticker'
        elif type == "China":
            firstpage=baseurl+'&f=cap_largeover,geo_china'
        finalurl=firstpage
        headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
        resp=requests.get(finalurl,headers=headers)
        soup = bs.BeautifulSoup(resp.text,"lxml")
        table=soup.findAll('td',{'class':'screener-body-table-nw'})
        maxpage_a_tag=[]
        maxpage_td=soup.findAll('td',{'class':'body-table'},bgcolor="#ffffff",valign="top",width="100%")
        tickerdict={}
        for tags in maxpage_td[0].findAll('a'):
             maxpage_a_tag.append(tags)
        if not maxpage_a_tag:
            maxpage=1
        elif (maxpage_a_tag[-1].text=="next"):
            maxpage=maxpage_a_tag[-2].text
        else:
            maxpage=maxpage_a_tag[-1].text
        for page in range(int(maxpage)):
            resp=requests.get(finalurl,headers=headers)
            soup = bs.BeautifulSoup(resp.text,"lxml")
            table=soup.findAll('td',{'class':'screener-body-table-nw'})
            for row in table:
                 ticker_rows=row.findAll('a',{'class':'screener-link-primary'},href=True)
                 for tickers in ticker_rows:
                     tickerdict[tickers.text]=tickers['href']
            items+=20
            finalurl="{}{}{}".format(firstpage,"&r=",str(items))
        print(tickerdict)


使用finviz_crawler().requesting('China')调用类

#1 楼

以下是一些改进代码的建议(不分先后顺序):

使变量具有局部性

该行:

items = 1


位于方法的顶部,但仅由方法底部的循环使用。

同样适用于:

tickerdict = {}


可以跨多个字符串中断行:

此:

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}


可以是:

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) '
    'AppleWebKit/537.36 (KHTML, like Gecko) '
    'Chrome/56.0.2924.87 Safari/537.36'
}


您通常不需要循环:

for tags in maxpage_td[0].findAll('a'):
    maxpage_a_tag.append(tags)


可能工作得很好,例如:

maxpage_a_tag.extend(maxpage_td[0].findAll('a'))


然后这个:

maxpage_a_tag = []
maxpage_a_tag.extend(maxpage_td[0].findAll('a'))


真的很:

maxpage_a_tag = list(maxpage_td[0].findAll('a'))


不要使用python名称

Python使用type这个名字。建议改成type_或更好,就像注释中所建议的那样,是更具描述性的内容,例如request_type。可能还有其他一些潜入其中的人。

class finviz_crawler():

    def requesting(self, type_):
        baseurl = 'https://finviz.com/screener.ashx?v=111'
        if type_ == 'sp500':
            firstpage = baseurl + '&f=idx_sp500&o=ticker'
        elif type_ == "China":
            firstpage = baseurl + '&f=cap_largeover,geo_china'
        else:
            raise ValueError("Unknown type {}".format(type_))
        finalurl = firstpage
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) '
                          'AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/56.0.2924.87 Safari/537.36'}
        resp = requests.get(finalurl, headers=headers)
        soup = bs.BeautifulSoup(resp.text, "lxml")
        maxpage_td = soup.findAll('td', {'class': 'body-table'},
                                  bgcolor="#ffffff", valign="top",
                                  width="100%")
        maxpage_a_tag = list(maxpage_td[0].findAll('a'))
        if not maxpage_a_tag:
            maxpage = 1
        elif (maxpage_a_tag[-1].text == "next"):
            maxpage = maxpage_a_tag[-2].text
        else:
            maxpage = maxpage_a_tag[-1].text

        items = 1
        tickerdict = {}
        for page in range(int(maxpage)):
            resp = requests.get(finalurl, headers=headers)
            soup = bs.BeautifulSoup(resp.text, "lxml")
            table = soup.findAll('td', {'class': 'screener-body-table-nw'})
            find_all_args = 'a', {'class': 'screener-link-primary'}
            for row in table:
                tickerdict.update(
                    {tickers.text: tickers['href']
                     for tickers in row.findAll(*find_all_args, href=True)})
                items += 20
            finalurl = "{}{}{}".format(firstpage, "&r=", str(items))

        for data in tickerdict.items():
            print(data)


finviz_crawler().requesting('China')


评论


\ $ \ begingroup \ $
@Michael好吧,根据PEP8,它应该是request_type,但是该名称确实可以更好地传达变量的含义。
\ $ \ endgroup \ $
–地狱
18年2月19日在9:43

\ $ \ begingroup \ $
maxpage_a_tag.extend(list(maxpage_td [0] .findAll('a')))您真的需要在此处转换为列表吗?
\ $ \ endgroup \ $
– Andrea Lazzarotto
18-2-19在20:17

\ $ \ begingroup \ $
@AndreaLazzarotto,可能不是扩展应该进行正确的迭代。在将整个内容移至一个列表分配的解释中,我倒向了这一行,并且没有删除列表强制转换。谢谢。
\ $ \ endgroup \ $
–斯蒂芬·劳赫(Stephen Rauch)
18年2月19日在20:27

#2 楼

除了@StephenRauch在其出色的回答中写的内容外,让我再添加一条评论:

您的课程完全没有用。不是它的功能很好,但是完全不需要class

它不从父级继承任何东西,也没有从其继承的子级,它没有属性,因此也没有状态,它拥有的一种方法甚至在任何地方都没有使用self。它不使用OOP的任何功能。换句话说,您也可以使该方法成为独立函数。而当您使用它时,也许可以通过传递baseurl作为参数来使其更可重用。

一个有价值的相关视频:停止编写类


也,请不要手工构建您的URL,请使用paramsrequests.get关键字:

type_params = {'sp500': {'f': 'idx_sp500', 'o': 'ticker'},
               'China': {'f': 'cap_largeover,geo_china'}}

url = 'https://finviz.com/screener.ashx'
params = {'v': 111}
params.update(type_params[type])
headers = {'User-Agent': ...}

resp = requests.get(url, headers=headers, params=params)


必要时,它甚至可以正确引用这些值。

评论


\ $ \ begingroup \ $
是的,是的,我才意识到,非常感谢!!!
\ $ \ endgroup \ $
–长途前进
18年2月19日在18:57

#3 楼

您还应该将代码拆分为功能部分,不要将IO(print(...))与生成结果混合在一起。

base_url

首先生成base_url

def make_base_url(request_type):
    baseurl='https://finviz.com/screener.ashx?v=111'
    if request_type == 'sp500':
        firstpage=baseurl+'&f=idx_sp500&o=ticker'
    elif request_type == "China":
        firstpage=baseurl+'&f=cap_largeover,geo_china'
    # What will happen when request_type is not `sp500` or `China`
    return firstpage  


输入不是sp500China的行为未定义时,这将引发异常

get_soup

HEADERS_DEFAULT = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}    

def get_soup(url, headers=HEADERS_DEFAULT):
    resp=requests.get(url,headers=headers)
    return bs.BeautifulSoup(resp.text,"lxml")

/>
出现网址时,它会得到汤料。

找到max_page

def find_max_page(base_soup):
    td_s = base_soup.findAll('td',{'class':'body-table'},bgcolor="#ffffff",valign="top",width="100%")
    a_s = td_s[0].findAll('a')
    if not a_s:
#         print('found nothing')
        return 1
    max_page = a_s.pop().text
    return int(max_page) if max_page != 'next' else int(a_s.pop().text)


进行int转换,其中最合乎逻辑的是

查找下一页的网址

def find_page_urls(base_url, max_pages):
    yield base_url
    for i in range(max_pages):
        items = str(1 + (i + 1) * 20)
        yield "{}&r={}".format(base_url, items)


提取行情记录

def get_ticker(url, headers=HEADERS_DEFAULT):
    soup = get_soup(url, headers)
    table=soup.findAll('td',{'class':'screener-body-table-nw'})
    for row in table:
         ticker_rows=row.findAll('a',{'class':'screener-link-primary'},href=True)
         for tickers in ticker_rows:
             yield tickers.text, tickers['href']


获取结果

def find_pages(urls, headers=HEADERS_DEFAULT):
    results = dict()
    for url in urls:
        results.update(get_ticker(url, headers))
    return results


将其放在一起

base_url = make_base_url('China')
base_soup = get_soup(base_url)
max_pages = find_max_page(base_soup)

urls = find_page_urls(base_url, max_pages, headers=HEADERS_DEFAULT)
result_dict = find_pages(urls)


如果您将其结构化这样,您可以分别测试每个部分,并在需要时手动调试