Avatar billede larsroan Nybegynder
14. juni 2006 - 11:04 Der er 7 kommentarer

Hvordan søke nedover i kode etter spesifik tekst?

Hei!

http://no.php.net/fopen fant jeg en svært nyttig funksjon for å hente info fra eksterne websider.  Med den lille koden like nedenfor henter php all kode inn i en matrise og "dumper den ut" på egen side.  Nyttig funksjon!

Jeg er en tidligere student fra China Textile University som er interessert i å hente ut info om endringer i en prisindex for bomuld i Kina.  Men hvordan skal jeg så søke igjennom koden til den eksterne websiden, ned til den rette linjen, og hente ut info om siste index-verdi?  Med denne koden på egen server så henter du hele websiden hos China Cotton Index og dumper den ut på egen webside;

<?php
function get_content($url)  {
  $ch = curl_init();
  curl_setopt ($ch, CURLOPT_URL, $url);
  curl_setopt ($ch, CURLOPT_HEADER, 0);
  ob_start();
  curl_exec ($ch);
  curl_close ($ch);
  $string = ob_get_contents();
  ob_end_clean();
  return $string;   
}
$content = get_content ("http://www.cncotton.com/price/cncottonindex_en.asp");
var_dump ($content);
?>

Det er akkurat denne koden som ligger på http://home.no.net/laroald/webhente.php - og som viser siste Cotton Index.  Men hvordan (1) søke (linje for linje eller?) ned til koden "CNCotton B", (2) hente siste dato og (3) index-verdi - som alltid er 4 og 5 linjer nedenfor "CNCotton B"?

Skal jeg benytte readline eller foreach eller?  Jeg har ikke lykkes med noen av disse. 

Etter å ha funnet "CNCotton B" kan det være en ide å søke videre etter "center">" og så hente ut de neste 10 tegnene?  For så igjen å søke etter "center">" og hente ut Index-verdi (5 tegn)?  Men hva er optimal måte å gjøre dette?

Her er koden midt nede i siden http://www.cncotton.com/price/cncottonindex_en.asp som jeg fokuserer på. Det er verdiene "2006-6-12" (dagens dato eller 1,2, max 3 dater tilbake) og index "14567" jeg vil søke frem til:

.......
        <td height="22" valign="top" bgcolor="#CDD8FC" class="ss1">
      <div align="center"><b>CNCotton B</b></div>
    </td>
  </tr>
  <tr bordercolor="#666666">
    <td> <div align="center">2006-6-12</div></td>
    <td> <div align="center">14567</div></td>
        <td> <div align="center">14081</div></td>
  </tr>
........

På forhånd; takk for hjelpen.

hilsen
Lars Roan
Bergen, N.
Avatar billede gizmo-gizmo Nybegynder
14. juni 2006 - 11:51 #1
Jeg ville starter med at fjerne ALLE tags, og så benytte en regular expression til at fange de dele ud du skal bruge. Her er et eksempel på hvordan det kan gøres:

<?php
function get_content($url)  {
  $ch = curl_init();
  curl_setopt ($ch, CURLOPT_URL, $url);
  curl_setopt ($ch, CURLOPT_HEADER, 0);
  ob_start();
  curl_exec ($ch);
  curl_close ($ch);
  $string = ob_get_contents();
  ob_end_clean();
  return $string;   
}
$content = get_content ("http://www.cncotton.com/price/cncottonindex_en.asp");

$content = strip_tags($content); //Fjern alle tags

preg_match_all("/([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})[^0-9]*([0-9]+)[^0-9]*([0-9]+)/", $content, $resultat); //Forsøg at matche "yyyy-m-d prisA prisB"

//Skriv resultat ud
for($i=0; $i<count($resultat[0]); $i++)
{
    echo "Dato: " . $resultat[1][$i] . "<br>";
    echo "CottonA: " . $resultat[2][$i] . "<br>";
    echo "CottonB " . $resultat[3][$i] . "<hr>";
}
?>
Avatar billede larsroan Nybegynder
14. juni 2006 - 15:58 #2
Flott - det hele fungerer!

preg_match_all  ser ut til å være en nyttig funksjon for behandling av "array of strings". Kompleks/kaotisk uttrykk er første inntrykk - men bit for bit er det forståelig.  Særlig etter at jeg fant denne artikkelen; http://weblogtoolscollection.com/regex/regex.php   

Nå er det bare å ta det hele i bruk. Takk for hjelpen.
Avatar billede gizmo-gizmo Nybegynder
14. juni 2006 - 16:16 #3
Super, no problem :-)
Yep, regular expressions kan godt virke lidt uforståelige ved første øjekast, men det er faktisk vildt nyttigt til at trække ting ud, validere osv.
Avatar billede larsroan Nybegynder
14. juni 2006 - 22:09 #4
Et lite tillegs-spm; Hvordan gjor du det, rent praktisk, efter å ha strippet $content for tags?  Jeg er ret og slet ikke helt sikker på hva vi søker efter med koden nedenfor. Derfor er spm'et;  Hvordan får du på enklest måte tilgang på $content efter at du har stippet den for html-kode - slik at du kan gå videre og utforme koden for preg_match_all? 
Når den gjelder regular expressions fant jeg nyttig info om denne pearl teknikken her; http://perldoc.perl.org/perlretut.html

Dato var relativt enkelt at tyde, men hva med:  [^0-9] * ([0-9]+)  ?
            /[^0-9]/;  # matches a non-numeric character
            a+ = match 'a' 1 or more times, i.e., at least once
            det vil si at  [0-9]+    betyr et tal - en eller flere ganger, minst en gang.
Hvilke non-numeric char. søker  /[^0-9]/  efter?    Og er '*' et generelt trunkering-tegn?

Til slut;  lengst nede på siden, http://www.cncotton.com/price/cncottonindex_en.asp , er det mulighet for å hente tidligere index-verdier.  Har du/dere forslag til en kode for at lagre disse index-verdiene på egen server, hente ut verdier for alle dager som har gåt siden sist siden blev åpnet (via koblingen nederst) og så lagre det på egen server, f.eks i en xml-fil?  Da hadde det vært mulig å presenter det hele som en komplet tidsserie, f.eks i et enkelt diagram.
Uanset; tusen takk for hjelpen gizmo-gizmo!
Avatar billede gizmo-gizmo Nybegynder
14. juni 2006 - 22:30 #5
Det var i virkeligheden ret enkelt. Jeg strippede alle tags og skrev resultat ud på skærmen, for at se om jeg kunne bruge det til noget. Jeg kom frem til at data'erne optræder sådan her:
2006-6-14 14563 14081 2006-6-13 14565 14081
altså yyyy-m-dd tal1 tal2 .. og så forfra y....

Og sådan en streng er sådan set ret nem at matche (på en lidt mere korrekt måde end jeg gjorde først):

\d{4}  (\d betyder digit - et tal 4 gange som årstallet jo må være)
-
\d{1,2}  (måneden, et tal enten 1 eller 2 gange (ved ikke om det er 1-12 eller 01-12))
-
\d{1,2} (samme)
\s+  (\s betyder en eller anden form for mellemrum (tag, space, linjeskift) som nødvendigvis må være efter datoen og før prisA/cottonA)
\d+  (et tal én eller flere gange)
\s+  (et mellem mere mellem prisA og prisB)
\d+  (igen, et tal én eller flere gange)

Tilsammen bliver det:
(\d{4}-\d{1,2}-\d{1,2})\s+(\d+)\s+(\d+)

Håber det er lidt lettere at forstå. Parenteserne () gør at man hiver værdierne ud.

For at hive tidligere værdier ud kunne man f.eks. lave et loop der henter side for side. Sådan ser URL'en ud:
http://www.cncotton.com/price/CNCottonindex_en.asp?page=1
http://www.cncotton.com/price/CNCottonindex_en.asp?page=2
osv.


Man kunne godt gemme dem i en xml fil (selvom jeg ikke ved hvordan :-), eller en mysql database, eller simpelthen bare i en tekstfil.
Avatar billede larsroan Nybegynder
16. juni 2006 - 09:56 #6
Takk for forklaringen Gizmo-Gizmo! 
Med bare disse reglene skal det være mulig å søke svært spesifikt i tilsvarende web-sider.  Du har også ret i at det er bare å lagre index-verdiene f.eks. i en xml fil, hente de inn, finne sist dato og bruke en loop bagover i siderne til denne dato (eller tidligere) eksisterer.  I praksis vil det si max 2-3 sider.  Nå fungerer det hele -takket være din hjelp!
Avatar billede gizmo-gizmo Nybegynder
16. juni 2006 - 10:08 #7
Det var så lidt :-)
Og ja du har ret, regular expressions er ekstremt kraftfulde når man skal matche (specielt trække data ud) strenge som i dit tilfælde, faktisk helt uunværligt :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester