2017-07-06 7 views
2

私はスクレイパークラスを持っています。これは人気のあるオッズポータルのプレーヤーを集めています。 正確なファーストネームは存在しません。サイトは短い形式を使用します。ラファエル - > Rのようなものです。幸いなことに、それらは脈打った形(ナダル - ラファエル)のリンクで見つけることができます。htmlからのデータの抽出

私は方法processPlayersを作成しました。これは単純なケースでは機能しますが、プレーヤーがファーストネームにハイフンを持つ場合や、ファーストネームが2つある場合には失敗します。

私はhtml構造とその問題を示すいくつかのテストを書いています。

class Scraper { 
 
    /** 
 
    * Converts a html string to a cheerio object 
 
    * @param {String} html The html string 
 
    * @return {Object} The cheerio object 
 
    */ 
 
    htmlToDom(html) { 
 
    return cheerio.load(html) 
 
    } 
 
    /** 
 
    * Gives back the number of parts if the name would slugify 
 
    * It takes in account, that the name could contains hyphen 
 
    * Leopold von Sacher-Masoch -> leopold-von-sacher-masoch 
 
    * @param {Array} a_name The name splitted by space (' ') 
 
    * @return {Integer} The length of the name 
 
    */ 
 
    getNameLength(a_name) { 
 
    let name = a_name.length > 1 ? a_name.join(' ') : a_name[0] 
 
    return a_name.length + name.split('-').length - 1 
 
    } 
 

 
    capitalize(a_name) { 
 
    let res = [] 
 
    a_name.forEach(str => { 
 
     res.push(str.substr(0, 1).toUpperCase() + str.substr(1)) 
 
    }) 
 
    return res.join(' ') 
 
    } 
 

 
    processPlayers(players) { 
 
    let link = players('a') 
 
    let href = link.attr('href') 
 
    let a_players = link.text().split(' - ') 
 
    let a_href = href.split('/') 
 
    let a_link = a_href[a_href.length - 2].split('-') 
 
    let a_player1 = a_players[0].trim().split(' ') 
 
    let a_player2 = a_players[1].trim().split(' ') 
 
    let a_player1_lastName = a_player1.slice(0, -1) 
 
    let a_player2_lastName = a_player2.slice(0, -1) 
 
    let a_player1ShortFirstName = [a_player1[a_player1.length - 1]] 
 
    let a_player2ShortFirstName = [a_player2[a_player2.length - 1]] 
 

 
    let p1_lnLength = this.getNameLength(a_player1_lastName) 
 
    let p1_fnLength = this.getNameLength(a_player1ShortFirstName) 
 
    let p2_lnLength = this.getNameLength(a_player2_lastName) 
 
    let p2_fnLength = this.getNameLength(a_player2ShortFirstName) 
 
    let p1_length = p1_lnLength + p1_fnLength 
 
    let p2_length = p2_lnLength + p2_fnLength 
 
    let player1FirstName = this.capitalize(a_link.slice(p1_lnLength, p1_length)) 
 
    let player2FirstName = this.capitalize(a_link.slice(p1_length + p2_lnLength, p1_length + p2_length)) 
 

 
    return { 
 
     p1: { 
 
     firstName: player1FirstName, 
 
     lastName: a_player1_lastName.join(' ') 
 
     }, 
 
     p2: { 
 
     firstName: player2FirstName, 
 
     lastName: a_player2_lastName.join(' ') 
 
     } 
 
    } 
 
    } 
 
} 
 

 
// test =========================================================== 
 

 

 
test('simple case', function() { 
 
    let playersCell = ` 
 
    <td> 
 
     <a href="/t/pavlyuchenkova-anastasia-sorribes-tormo-sara/"> 
 
     <span>Pavlyuchenkova A.</span> 
 
     - Sorribes Tormo S. 
 
     </a> 
 
    </td> 
 
    ` 
 
    const scraper = new Scraper() 
 
    const td = scraper.htmlToDom(playersCell) 
 
    const players = scraper.processPlayers(td) 
 

 
    equal(players.p1.firstName, 'Anastasia') 
 
    equal(players.p1.lastName, 'Pavlyuchenkova') 
 

 
    deepEqual(players.p2, { 
 
    firstName: 'Sara', 
 
    lastName: 'Sorribes Tormo', 
 
    }) 
 
}); 
 

 
// ===================================================================== 
 

 
test('hyphen in last name', function() { 
 
    let playersCell = ` 
 
    <td><a href="/t/kudermetova-veronika-duque-marino-mariana/"> 
 
     <span>Kudermetova V.</span> - Duque-Marino M.</a> 
 
    </td> 
 
    ` 
 
    const scraper = new Scraper() 
 
    const td = scraper.htmlToDom(playersCell) 
 
    const players = scraper.processPlayers(td) 
 

 
    equal(players.p2.firstName, 'Mariana') 
 
    equal(players.p2.lastName, 'Duque-Marino') 
 
}); 
 

 
// ===================================================================== 
 

 
test('hyphen in first name', function() { 
 
    let playersCell = ` 
 
    <td> 
 
     <a href="/t/tsonga-jo-wilfried-mayer-florian/"> 
 
     <span>Tsonga J-W.</span> 
 
     - Mayer F. 
 
     </a> 
 
    </td> 
 
    ` 
 
    const scraper = new Scraper() 
 
    const td = scraper.htmlToDom(playersCell) 
 
    const players = scraper.processPlayers(td) 
 

 
    equal(players.p1.firstName, 'Jo-Wilfried') 
 
    equal(players.p1.lastName, 'Tsonga') 
 
}); 
 

 
test('two first names', function() { 
 
    let playersCell = ` 
 
     <td> 
 
     <a href="/t/alexandrova-ekaterina-muguruza-blanco-garbine/"> 
 
     Alexandrova E. - <span>Muguruza</span> B. G.</a> 
 
     </td> 
 
    ` 
 
    const scraper = new Scraper() 
 
    const td = scraper.htmlToDom(playersCell) 
 
    const players = scraper.processPlayers(td) 
 

 
    equal(players.p2.firstName, 'Blanco Garbine') 
 
    equal(players.p2.lastName, 'Muguruza') 
 
});
<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <meta charset=utf-8 /> 
 
    <title></title> 
 
    <script src="https://wzrd.in/standalone/[email protected]"></script> 
 
    <link rel="StyleSheet" href="http://code.jquery.com/qunit/qunit-1.12.0.css" type="text/css"> 
 
</head> 
 

 
<body> 
 
    <div id="qunit"></div> 
 
    <div id="qunit-fixture"></div> 
 
    <script src="http://code.jquery.com/qunit/qunit-1.12.0.js" type="text/javascript"> 
 
    </script> 
 
</body> 
 

 
</html>

stackoverflowののスニペットはqunitといくつかの問題を抱えているように見えます。だからここJsBinリンクは次のとおりです。問題はlast nameと「難しい」のケースで正しくfirst nameを決定することであるように

http://jsbin.com/tohafiqivo/1/edit?html,js,output

答えて

1

が見えます。

あなたは2つのフルネームでテキストを分割しています。
次に、フルネームを複数の単語に分割しています。

そして、first nameが常に最後の単語で、残りがlast nameであると仮定しています。

実際、last nameはすべて短い形式ではない単語で、残りはfirst nameです。

私はこの方法であなたの問題を解決しました。

(私はもう使用されていない機能を削除しました)以下の更新class Scraperを見つけてください:

class Scraper { 
    /** 
    * Converts a html string to a cheerio object 
    * @param {String} html The html string 
    * @return {Object} The cheerio object 
    */ 
    htmlToDom(html) { 
    return cheerio.load(html) 
    } 

    processPlayers(players) { 
    let href = players('a').attr('href') 
    let N = players('a').text().trim().split(/\s+-\s+/).map(n => { 
     let r = new RegExp('.+('+n.replace(/-/g,'\\S+').replace(/\./g,'[^-]+').replace(/\s/g,'.')+').+','i') 
     let p = {lastName: n.replace(/\s\S+\./g,'')} 
     href.match(r).map(m => { 
      r = new RegExp(p.lastName.replace(/\s/g,'.') + '-', 'i') 
      m = m.replace(r,'').split(/-/).map(i => {return i.substring(0,1).toUpperCase() + i.substring(1)}) 
      p.firstName = n.split(p.lastName + ' ')[1].replace(/\./g,'').split('').map(l => {return m[0] && m[0].indexOf(l) === 0 ? m.shift() : l}).join('') 
     }) 
     return p 
     }) 
    return {p1: N[0], p2: N[1]} 
    } 
} 

それはすべてのテストに合格。 非常に読みにくい場合は申し訳ありません。

+0

読んで理解しにくいですが、非常にコンパクトな(作業中の)ソリューションです:) – user3568719

関連する問題